사용하지 않는 파일 정리 전 백업 (Phase 10 완료 후 상태)
This commit is contained in:
@@ -4,14 +4,27 @@ 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/overview/overview_screen.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/license/license_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/inventory/inventory_dashboard.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/rent_dashboard.dart';
|
||||
import 'package:superport/screens/rent/controllers/rent_controller.dart';
|
||||
import 'package:superport/services/auth_service.dart';
|
||||
import 'package:superport/services/dashboard_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';
|
||||
|
||||
@@ -21,8 +34,7 @@ import 'package:superport/data/models/auth/auth_user.dart';
|
||||
class AppLayout extends StatefulWidget {
|
||||
final String initialRoute;
|
||||
|
||||
const AppLayout({Key? key, this.initialRoute = Routes.home})
|
||||
: super(key: key);
|
||||
const AppLayout({super.key, this.initialRoute = Routes.home});
|
||||
|
||||
@override
|
||||
State<AppLayout> createState() => _AppLayoutState();
|
||||
@@ -35,10 +47,9 @@ class _AppLayoutState extends State<AppLayout>
|
||||
late AnimationController _sidebarAnimationController;
|
||||
AuthUser? _currentUser;
|
||||
late final AuthService _authService;
|
||||
late final DashboardService _dashboardService;
|
||||
late final LookupsService _lookupsService;
|
||||
late Animation<double> _sidebarAnimation;
|
||||
int _expiringLicenseCount = 0; // 7일 내 만료 예정 라이선스 수
|
||||
int _expiringMaintenanceCount = 0; // 30일 내 만료 예정 유지보수 수
|
||||
|
||||
// 레이아웃 상수 (1920x1080 최적화)
|
||||
static const double _sidebarExpandedWidth = 260.0;
|
||||
@@ -52,10 +63,9 @@ class _AppLayoutState extends State<AppLayout>
|
||||
_currentRoute = widget.initialRoute;
|
||||
_setupAnimations();
|
||||
_authService = GetIt.instance<AuthService>();
|
||||
_dashboardService = GetIt.instance<DashboardService>();
|
||||
_lookupsService = GetIt.instance<LookupsService>();
|
||||
_loadCurrentUser();
|
||||
_loadLicenseExpirySummary();
|
||||
_loadMaintenanceAlerts();
|
||||
_initializeLookupData(); // Lookup 데이터 초기화
|
||||
}
|
||||
|
||||
@@ -68,34 +78,19 @@ class _AppLayoutState extends State<AppLayout>
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadLicenseExpirySummary() async {
|
||||
Future<void> _loadMaintenanceAlerts() async {
|
||||
try {
|
||||
print('[DEBUG] 라이선스 만료 정보 로드 시작...');
|
||||
final result = await _dashboardService.getLicenseExpirySummary();
|
||||
result.fold(
|
||||
(failure) {
|
||||
// 실패 시 0으로 유지
|
||||
print('[ERROR] 라이선스 만료 정보 로드 실패: $failure');
|
||||
},
|
||||
(summary) {
|
||||
print('[DEBUG] 라이선스 만료 정보 로드 성공!');
|
||||
print('[DEBUG] 7일 내 만료: ${summary.expiring7Days}개');
|
||||
print('[DEBUG] 30일 내 만료: ${summary.expiring30Days}개');
|
||||
print('[DEBUG] 90일 내 만료: ${summary.expiring90Days}개');
|
||||
print('[DEBUG] 이미 만료: ${summary.expired}개');
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
// 30일 내 만료 수를 표시 (7일 내 만료가 포함됨)
|
||||
// expiring_30_days는 30일 이내의 모든 라이선스를 포함
|
||||
_expiringLicenseCount = summary.expiring30Days;
|
||||
print('[DEBUG] 상태 업데이트 완료: $_expiringLicenseCount (30일 내 만료)');
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
print('[DEBUG] 유지보수 알림 정보 로드 시작...');
|
||||
// TODO: MaintenanceController를 통해 알림 정보 로드
|
||||
// 현재는 임시로 0으로 설정
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_expiringMaintenanceCount = 0;
|
||||
print('[DEBUG] 유지보수 알림 상태 업데이트 완료: $_expiringMaintenanceCount');
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print('[ERROR] 라이선스 만료 정보 로드 중 예외 발생: $e');
|
||||
print('[ERROR] 유지보수 알림 정보 로드 중 예외 발생: $e');
|
||||
print('[ERROR] 스택 트레이스: ${StackTrace.current}');
|
||||
}
|
||||
}
|
||||
@@ -155,6 +150,13 @@ class _AppLayoutState extends State<AppLayout>
|
||||
switch (route) {
|
||||
case Routes.home:
|
||||
return const OverviewScreen();
|
||||
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:
|
||||
@@ -164,10 +166,45 @@ class _AppLayoutState extends State<AppLayout>
|
||||
return const CompanyList();
|
||||
case Routes.user:
|
||||
return const UserList();
|
||||
case Routes.license:
|
||||
return const LicenseList();
|
||||
// License 시스템이 Maintenance로 대체됨
|
||||
case Routes.maintenance:
|
||||
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.inventoryDashboard:
|
||||
return const InventoryDashboard();
|
||||
case Routes.rent:
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => GetIt.instance<RentController>(),
|
||||
child: const RentListScreen(),
|
||||
);
|
||||
case Routes.rentDashboard:
|
||||
return ChangeNotifierProvider(
|
||||
create: (_) => GetIt.instance<RentController>(),
|
||||
child: const RentDashboard(),
|
||||
);
|
||||
case '/test/api':
|
||||
// Navigator를 사용하여 별도 화면으로 이동
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
@@ -184,9 +221,9 @@ class _AppLayoutState extends State<AppLayout>
|
||||
setState(() {
|
||||
_currentRoute = route;
|
||||
});
|
||||
// 라이선스 화면으로 이동할 때 만료 정보 새로고침
|
||||
if (route == Routes.license) {
|
||||
_loadLicenseExpirySummary();
|
||||
// 유지보수 화면으로 이동할 때 알림 정보 새로고침
|
||||
if (route == Routes.maintenance || route == Routes.maintenanceAlert) {
|
||||
_loadMaintenanceAlerts();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,57 +241,7 @@ class _AppLayoutState extends State<AppLayout>
|
||||
}
|
||||
|
||||
/// 현재 페이지 제목 가져오기
|
||||
String _getPageTitle() {
|
||||
switch (_currentRoute) {
|
||||
case Routes.home:
|
||||
return '대시보드';
|
||||
case Routes.equipment:
|
||||
case Routes.equipmentInList:
|
||||
case Routes.equipmentOutList:
|
||||
case Routes.equipmentRentList:
|
||||
return '장비 관리';
|
||||
case Routes.company:
|
||||
return '회사 관리';
|
||||
case Routes.user:
|
||||
return '사용자 관리';
|
||||
case Routes.license:
|
||||
return '유지보수 관리';
|
||||
case Routes.warehouseLocation:
|
||||
return '입고지 관리';
|
||||
case '/test/api':
|
||||
return 'API 테스트';
|
||||
default:
|
||||
return '대시보드';
|
||||
}
|
||||
}
|
||||
|
||||
/// 브레드크럼 경로 가져오기
|
||||
List<String> _getBreadcrumbs() {
|
||||
switch (_currentRoute) {
|
||||
case Routes.home:
|
||||
return ['홈', '대시보드'];
|
||||
case Routes.equipment:
|
||||
return ['홈', '장비 관리', '전체'];
|
||||
case Routes.equipmentInList:
|
||||
return ['홈', '장비 관리', '입고'];
|
||||
case Routes.equipmentOutList:
|
||||
return ['홈', '장비 관리', '출고'];
|
||||
case Routes.equipmentRentList:
|
||||
return ['홈', '장비 관리', '대여'];
|
||||
case Routes.company:
|
||||
return ['홈', '회사 관리'];
|
||||
case Routes.user:
|
||||
return ['홈', '사용자 관리'];
|
||||
case Routes.license:
|
||||
return ['홈', '유지보수 관리'];
|
||||
case Routes.warehouseLocation:
|
||||
return ['홈', '입고지 관리'];
|
||||
case '/test/api':
|
||||
return ['홈', '개발자 도구', 'API 테스트'];
|
||||
default:
|
||||
return ['홈', '대시보드'];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -560,122 +547,12 @@ class _AppLayoutState extends State<AppLayout>
|
||||
currentRoute: _currentRoute,
|
||||
onRouteChanged: _navigateTo,
|
||||
collapsed: _sidebarCollapsed,
|
||||
expiringLicenseCount: _expiringLicenseCount,
|
||||
expiringMaintenanceCount: _expiringMaintenanceCount,
|
||||
);
|
||||
}
|
||||
|
||||
/// F-Pattern 2차 시선: 페이지 헤더 (간소화된 제목)
|
||||
Widget _buildPageHeader() {
|
||||
final breadcrumbs = _getBreadcrumbs(); // 변수는 유지 (향후 사용 가능)
|
||||
|
||||
return Container(
|
||||
// BaseListScreen과 동일한 폭을 위해 마진 추가
|
||||
margin: const EdgeInsets.symmetric(horizontal: ShadcnTheme.spacing6), // 24px 마진 추가
|
||||
padding: const EdgeInsets.all(ShadcnTheme.spacing3), // 12px 내부 패딩
|
||||
decoration: BoxDecoration(
|
||||
color: ShadcnTheme.background,
|
||||
borderRadius: BorderRadius.circular(ShadcnTheme.radiusLg),
|
||||
border: Border.all(
|
||||
color: ShadcnTheme.border,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 페이지 제목 (좌측 패딩 + 작은 텍스트)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: ShadcnTheme.spacing2), // 좌측 패딩 추가
|
||||
child: Text(
|
||||
_getPageTitle(),
|
||||
style: ShadcnTheme.bodySmall.copyWith(
|
||||
fontWeight: FontWeight.w500, // 약간 두껴게
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// 브레드크럼 (주석처리)
|
||||
/*
|
||||
const SizedBox(height: ShadcnTheme.spacing1),
|
||||
// 브레드크럼
|
||||
Row(
|
||||
children: [
|
||||
for (int i = 0; i < breadcrumbs.length; i++) ...[
|
||||
if (i > 0) ...[
|
||||
const SizedBox(width: ShadcnTheme.spacing1),
|
||||
Icon(
|
||||
Icons.chevron_right,
|
||||
size: 14,
|
||||
color: ShadcnTheme.foregroundSubtle,
|
||||
),
|
||||
const SizedBox(width: ShadcnTheme.spacing1),
|
||||
],
|
||||
Text(
|
||||
breadcrumbs[i],
|
||||
style: i == breadcrumbs.length - 1
|
||||
? ShadcnTheme.bodySmall.copyWith(
|
||||
color: ShadcnTheme.foreground,
|
||||
fontWeight: FontWeight.w500,
|
||||
)
|
||||
: ShadcnTheme.bodySmall.copyWith(
|
||||
color: ShadcnTheme.foregroundMuted,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
*/
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 리스트 페이지 여부 확인
|
||||
bool _isListPage() {
|
||||
return [
|
||||
Routes.equipment,
|
||||
Routes.equipmentInList,
|
||||
Routes.equipmentOutList,
|
||||
Routes.equipmentRentList,
|
||||
Routes.company,
|
||||
Routes.user,
|
||||
Routes.license,
|
||||
Routes.warehouseLocation,
|
||||
].contains(_currentRoute);
|
||||
}
|
||||
|
||||
/// 추가 액션 처리
|
||||
void _handleAddAction() {
|
||||
String addRoute = '';
|
||||
switch (_currentRoute) {
|
||||
case Routes.equipment:
|
||||
case Routes.equipmentInList:
|
||||
addRoute = '/equipment/in';
|
||||
break;
|
||||
case Routes.equipmentOutList:
|
||||
addRoute = '/equipment/out';
|
||||
break;
|
||||
case Routes.company:
|
||||
addRoute = '/company/add';
|
||||
break;
|
||||
case Routes.user:
|
||||
addRoute = '/user/add';
|
||||
break;
|
||||
case Routes.license:
|
||||
addRoute = '/license/add';
|
||||
break;
|
||||
case Routes.warehouseLocation:
|
||||
addRoute = '/warehouse-location/add';
|
||||
break;
|
||||
}
|
||||
if (addRoute.isNotEmpty) {
|
||||
Navigator.pushNamed(context, addRoute).then((result) {
|
||||
if (result == true) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 프로필 메뉴 표시
|
||||
void _showProfileMenu(BuildContext context) {
|
||||
@@ -862,15 +739,15 @@ class SidebarMenu extends StatelessWidget {
|
||||
final String currentRoute;
|
||||
final Function(String) onRouteChanged;
|
||||
final bool collapsed;
|
||||
final int expiringLicenseCount;
|
||||
final int expiringMaintenanceCount;
|
||||
|
||||
const SidebarMenu({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.currentRoute,
|
||||
required this.onRouteChanged,
|
||||
required this.collapsed,
|
||||
required this.expiringLicenseCount,
|
||||
}) : super(key: key);
|
||||
required this.expiringMaintenanceCount,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -910,6 +787,22 @@ class SidebarMenu extends StatelessWidget {
|
||||
badge: null,
|
||||
),
|
||||
|
||||
_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: '장비 관리',
|
||||
@@ -923,6 +816,25 @@ class SidebarMenu extends StatelessWidget {
|
||||
badge: null,
|
||||
),
|
||||
|
||||
_buildMenuItem(
|
||||
icon: Icons.history,
|
||||
title: '재고 이력',
|
||||
route: Routes.inventoryHistory,
|
||||
isActive: [
|
||||
Routes.inventory,
|
||||
Routes.inventoryHistory,
|
||||
].contains(currentRoute),
|
||||
badge: null,
|
||||
),
|
||||
|
||||
_buildMenuItem(
|
||||
icon: Icons.analytics_outlined,
|
||||
title: '재고 대시보드',
|
||||
route: Routes.inventoryDashboard,
|
||||
isActive: currentRoute == Routes.inventoryDashboard,
|
||||
badge: null,
|
||||
),
|
||||
|
||||
_buildMenuItem(
|
||||
icon: Icons.warehouse_outlined,
|
||||
title: '입고지 관리',
|
||||
@@ -931,6 +843,14 @@ class SidebarMenu extends StatelessWidget {
|
||||
badge: null,
|
||||
),
|
||||
|
||||
_buildMenuItem(
|
||||
icon: Icons.location_on_outlined,
|
||||
title: '우편번호 검색',
|
||||
route: Routes.zipcode,
|
||||
isActive: currentRoute == Routes.zipcode,
|
||||
badge: null,
|
||||
),
|
||||
|
||||
_buildMenuItem(
|
||||
icon: Icons.business_outlined,
|
||||
title: '회사 관리',
|
||||
@@ -948,12 +868,34 @@ class SidebarMenu extends StatelessWidget {
|
||||
),
|
||||
|
||||
_buildMenuItem(
|
||||
icon: Icons.support_outlined,
|
||||
icon: Icons.build_circle_outlined,
|
||||
title: '유지보수 관리',
|
||||
route: Routes.license,
|
||||
isActive: currentRoute == Routes.license,
|
||||
badge: expiringLicenseCount > 0 ? expiringLicenseCount.toString() : null,
|
||||
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.maintenanceSchedule,
|
||||
isActive: currentRoute == Routes.maintenanceSchedule,
|
||||
),
|
||||
_buildSubMenuItem(
|
||||
title: '알림 대시보드',
|
||||
route: Routes.maintenanceAlert,
|
||||
isActive: currentRoute == Routes.maintenanceAlert,
|
||||
),
|
||||
_buildSubMenuItem(
|
||||
title: '이력 조회',
|
||||
route: Routes.maintenanceHistory,
|
||||
isActive: currentRoute == Routes.maintenanceHistory,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
if (!collapsed) ...[
|
||||
const SizedBox(height: ShadcnTheme.spacing4),
|
||||
@@ -1024,51 +966,55 @@ class SidebarMenu extends StatelessWidget {
|
||||
required String route,
|
||||
required bool isActive,
|
||||
String? badge,
|
||||
bool hasSubMenu = false,
|
||||
List<Widget> subMenuItems = const [],
|
||||
}) {
|
||||
return 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,
|
||||
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: Row(
|
||||
children: [
|
||||
Icon(
|
||||
isActive ? _getFilledIcon(icon) : icon,
|
||||
size: 20,
|
||||
color: isActive
|
||||
? ShadcnTheme.primary
|
||||
: ShadcnTheme.foregroundSecondary,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: collapsed ? ShadcnTheme.spacing3 : ShadcnTheme.spacing3,
|
||||
vertical: ShadcnTheme.spacing2 + 2,
|
||||
),
|
||||
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,
|
||||
),
|
||||
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 (badge != null) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
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,
|
||||
@@ -1102,6 +1048,62 @@ class SidebarMenu extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1114,14 +1116,24 @@ class SidebarMenu extends StatelessWidget {
|
||||
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;
|
||||
default:
|
||||
return outlinedIcon;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class ShadcnCard extends StatefulWidget {
|
||||
final bool elevated;
|
||||
|
||||
const ShadcnCard({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.child,
|
||||
this.padding,
|
||||
this.margin,
|
||||
@@ -24,7 +24,7 @@ class ShadcnCard extends StatefulWidget {
|
||||
this.onTap,
|
||||
this.hoverable = true,
|
||||
this.elevated = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<ShadcnCard> createState() => _ShadcnCardState();
|
||||
@@ -89,7 +89,7 @@ class ShadcnButton extends StatefulWidget {
|
||||
final Color? textColor;
|
||||
|
||||
const ShadcnButton({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.text,
|
||||
this.onPressed,
|
||||
this.variant = ShadcnButtonVariant.primary,
|
||||
@@ -99,7 +99,7 @@ class ShadcnButton extends StatefulWidget {
|
||||
this.loading = false,
|
||||
this.backgroundColor,
|
||||
this.textColor,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<ShadcnButton> createState() => _ShadcnButtonState();
|
||||
@@ -336,7 +336,7 @@ class ShadcnInput extends StatefulWidget {
|
||||
final bool required;
|
||||
|
||||
const ShadcnInput({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.label,
|
||||
this.placeholder,
|
||||
this.errorText,
|
||||
@@ -352,7 +352,7 @@ class ShadcnInput extends StatefulWidget {
|
||||
this.enabled = true,
|
||||
this.maxLines = 1,
|
||||
this.required = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<ShadcnInput> createState() => _ShadcnInputState();
|
||||
@@ -480,12 +480,12 @@ class ShadcnBadge extends StatelessWidget {
|
||||
final Widget? icon;
|
||||
|
||||
const ShadcnBadge({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.text,
|
||||
this.variant = ShadcnBadgeVariant.primary,
|
||||
this.size = ShadcnBadgeSize.medium,
|
||||
this.icon,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -663,12 +663,12 @@ class ShadcnSeparator extends StatelessWidget {
|
||||
final EdgeInsetsGeometry? margin;
|
||||
|
||||
const ShadcnSeparator({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.direction = Axis.horizontal,
|
||||
this.thickness = 1.0,
|
||||
this.color,
|
||||
this.margin,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -691,14 +691,14 @@ class ShadcnAvatar extends StatelessWidget {
|
||||
final bool showBorder;
|
||||
|
||||
const ShadcnAvatar({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.imageUrl,
|
||||
this.initials,
|
||||
this.size = 40,
|
||||
this.backgroundColor,
|
||||
this.textColor,
|
||||
this.showBorder = true,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -752,14 +752,14 @@ class ShadcnChip extends StatelessWidget {
|
||||
final bool selected;
|
||||
|
||||
const ShadcnChip({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.label,
|
||||
this.backgroundColor,
|
||||
this.textColor,
|
||||
this.onDeleted,
|
||||
this.avatar,
|
||||
this.selected = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -821,13 +821,13 @@ class ShadcnProgress extends StatelessWidget {
|
||||
final bool showLabel;
|
||||
|
||||
const ShadcnProgress({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.value,
|
||||
this.height = 8,
|
||||
this.backgroundColor,
|
||||
this.valueColor,
|
||||
this.showLabel = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -17,7 +17,7 @@ class AutocompleteDropdown extends StatelessWidget {
|
||||
final Widget emptyWidget;
|
||||
|
||||
const AutocompleteDropdown({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.items,
|
||||
required this.inputText,
|
||||
required this.onSelect,
|
||||
@@ -27,7 +27,7 @@ class AutocompleteDropdown extends StatelessWidget {
|
||||
padding: EdgeInsets.all(12.0),
|
||||
child: Text('검색 결과가 없습니다'),
|
||||
),
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -12,13 +12,13 @@ class CategorySelectionField extends StatefulWidget {
|
||||
final bool isRequired;
|
||||
|
||||
const CategorySelectionField({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.category,
|
||||
required this.subCategory,
|
||||
required this.subSubCategory,
|
||||
required this.onCategoryChanged,
|
||||
this.isRequired = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<CategorySelectionField> createState() => _CategorySelectionFieldState();
|
||||
|
||||
@@ -10,12 +10,12 @@ class DatePickerField extends StatelessWidget {
|
||||
final bool isRequired;
|
||||
|
||||
const DatePickerField({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.selectedDate,
|
||||
required this.onDateChanged,
|
||||
this.allowFutureDate = false,
|
||||
this.isRequired = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -7,11 +7,11 @@ class FormFieldWrapper extends StatelessWidget {
|
||||
final bool isRequired;
|
||||
|
||||
const FormFieldWrapper({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.child,
|
||||
this.isRequired = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -12,12 +12,12 @@ class HighlightText extends StatelessWidget {
|
||||
final TextStyle? style;
|
||||
|
||||
const HighlightText({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.text,
|
||||
required this.highlight,
|
||||
this.highlightColor = Colors.blue,
|
||||
this.style,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -18,7 +18,7 @@ class BaseListScreen extends StatelessWidget {
|
||||
final IconData emptyIcon;
|
||||
|
||||
const BaseListScreen({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.headerSection,
|
||||
required this.searchBar,
|
||||
this.filterSection,
|
||||
@@ -30,7 +30,7 @@ class BaseListScreen extends StatelessWidget {
|
||||
this.onRefresh,
|
||||
this.emptyMessage = '데이터가 없습니다',
|
||||
this.emptyIcon = Icons.inbox_outlined,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -13,7 +13,7 @@ class FormLayoutTemplate extends StatelessWidget {
|
||||
final Widget? customActions;
|
||||
|
||||
const FormLayoutTemplate({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.child,
|
||||
this.onSave,
|
||||
@@ -22,7 +22,7 @@ class FormLayoutTemplate extends StatelessWidget {
|
||||
this.isLoading = false,
|
||||
this.showBottomButtons = true,
|
||||
this.customActions,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -108,12 +108,12 @@ class FormSection extends StatelessWidget {
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
const FormSection({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.title,
|
||||
this.subtitle,
|
||||
required this.children,
|
||||
this.padding,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -157,7 +157,7 @@ class FormSection extends StatelessWidget {
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
}).toList(),
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -172,12 +172,12 @@ class FormFieldWrapper extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const FormFieldWrapper({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.label,
|
||||
this.hint,
|
||||
this.required = false,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -27,13 +27,13 @@ class AddressInput extends StatefulWidget {
|
||||
final bool isRequired;
|
||||
|
||||
const AddressInput({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.initialZipCode = '',
|
||||
this.initialRegion = '',
|
||||
this.initialDetailAddress = '',
|
||||
required this.onAddressChanged,
|
||||
this.isRequired = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<AddressInput> createState() => _AddressInputState();
|
||||
|
||||
@@ -18,7 +18,7 @@ class AutocompleteDropdownField extends StatefulWidget {
|
||||
final bool enabled;
|
||||
|
||||
const AutocompleteDropdownField({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.items,
|
||||
@@ -27,7 +27,7 @@ class AutocompleteDropdownField extends StatefulWidget {
|
||||
this.isRequired = false,
|
||||
this.hintText = '',
|
||||
this.enabled = true,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<AutocompleteDropdownField> createState() =>
|
||||
|
||||
@@ -19,7 +19,7 @@ class CategoryAutocompleteField extends StatefulWidget {
|
||||
final bool enabled;
|
||||
|
||||
const CategoryAutocompleteField({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.hintText,
|
||||
required this.value,
|
||||
required this.items,
|
||||
@@ -27,7 +27,7 @@ class CategoryAutocompleteField extends StatefulWidget {
|
||||
this.isRequired = false,
|
||||
this.onChanged,
|
||||
this.enabled = true,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
State<CategoryAutocompleteField> createState() =>
|
||||
|
||||
@@ -13,12 +13,12 @@ class Pagination extends StatelessWidget {
|
||||
final ValueChanged<int> onPageChanged;
|
||||
|
||||
const Pagination({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.totalCount,
|
||||
required this.currentPage,
|
||||
required this.pageSize,
|
||||
required this.onPageChanged,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -12,7 +12,7 @@ class RemarkInput extends StatelessWidget {
|
||||
final bool enabled;
|
||||
|
||||
const RemarkInput({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.controller,
|
||||
this.label = '비고',
|
||||
this.hint = '비고를 입력하세요',
|
||||
@@ -20,7 +20,7 @@ class RemarkInput extends StatelessWidget {
|
||||
this.minLines = 4,
|
||||
this.maxLines,
|
||||
this.enabled = true,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -14,14 +14,14 @@ class StandardActionBar extends StatelessWidget {
|
||||
final String? statusMessage; // 추가 상태 메시지
|
||||
|
||||
const StandardActionBar({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.leftActions = const [],
|
||||
this.rightActions = const [],
|
||||
this.selectedCount,
|
||||
required this.totalCount,
|
||||
this.onRefresh,
|
||||
this.statusMessage,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -204,12 +204,12 @@ class StandardFilterDropdown<T> extends StatelessWidget {
|
||||
final String? hint;
|
||||
|
||||
const StandardFilterDropdown({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.value,
|
||||
required this.items,
|
||||
required this.onChanged,
|
||||
this.hint,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -33,7 +33,7 @@ class StandardDataTable extends StatelessWidget {
|
||||
final bool applyZebraStripes; // 짝수 행 배경색 적용 여부
|
||||
|
||||
const StandardDataTable({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.columns,
|
||||
required this.rows,
|
||||
this.showCheckbox = false,
|
||||
@@ -43,7 +43,7 @@ class StandardDataTable extends StatelessWidget {
|
||||
this.horizontalScrollController,
|
||||
this.emptyWidget,
|
||||
this.applyZebraStripes = true,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -178,7 +178,7 @@ class StandardDataRow extends StatelessWidget {
|
||||
final List<DataColumn> columns;
|
||||
|
||||
const StandardDataRow({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.index,
|
||||
required this.cells,
|
||||
this.showCheckbox = false,
|
||||
@@ -186,7 +186,7 @@ class StandardDataRow extends StatelessWidget {
|
||||
this.onSelect,
|
||||
this.applyZebraStripes = true,
|
||||
required this.columns,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -254,13 +254,13 @@ class StandardActionButtons extends StatelessWidget {
|
||||
final double buttonSize;
|
||||
|
||||
const StandardActionButtons({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.onView,
|
||||
this.onEdit,
|
||||
this.onDelete,
|
||||
this.customButtons,
|
||||
this.buttonSize = 32,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -7,9 +7,9 @@ class StandardLoadingState extends StatelessWidget {
|
||||
final String message;
|
||||
|
||||
const StandardLoadingState({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.message = '데이터를 불러오는 중...',
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -40,12 +40,12 @@ class StandardErrorState extends StatelessWidget {
|
||||
final IconData icon;
|
||||
|
||||
const StandardErrorState({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.title = '오류가 발생했습니다',
|
||||
this.message,
|
||||
this.onRetry,
|
||||
this.icon = Icons.error_outline,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -99,12 +99,12 @@ class StandardEmptyState extends StatelessWidget {
|
||||
final IconData icon;
|
||||
|
||||
const StandardEmptyState({
|
||||
Key? key,
|
||||
super.key,
|
||||
this.title = '데이터가 없습니다',
|
||||
this.message,
|
||||
this.action,
|
||||
this.icon = Icons.inbox_outlined,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -154,12 +154,12 @@ class StandardInfoMessage extends StatelessWidget {
|
||||
final VoidCallback? onClose;
|
||||
|
||||
const StandardInfoMessage({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.message,
|
||||
this.icon = Icons.info_outline,
|
||||
this.color,
|
||||
this.onClose,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -220,13 +220,13 @@ class StandardStatCard extends StatelessWidget {
|
||||
final String? subtitle;
|
||||
|
||||
const StandardStatCard({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.value,
|
||||
required this.icon,
|
||||
required this.color,
|
||||
this.subtitle,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -16,7 +16,7 @@ class UnifiedSearchBar extends StatelessWidget {
|
||||
final bool showSearchButton;
|
||||
|
||||
const UnifiedSearchBar({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.controller,
|
||||
this.placeholder = '검색어를 입력하세요...',
|
||||
required this.onSearch,
|
||||
@@ -25,7 +25,7 @@ class UnifiedSearchBar extends StatelessWidget {
|
||||
this.suffixButton,
|
||||
this.filters,
|
||||
this.showSearchButton = true,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
Reference in New Issue
Block a user