diff --git a/asset/image/location.svg b/asset/image/location.svg
new file mode 100644
index 0000000..3358301
--- /dev/null
+++ b/asset/image/location.svg
@@ -0,0 +1,6 @@
+
diff --git a/lib/Model/model/equipment/equipment_list_model.dart b/lib/Model/model/equipment/equipment_list_model.dart
new file mode 100644
index 0000000..40c9115
--- /dev/null
+++ b/lib/Model/model/equipment/equipment_list_model.dart
@@ -0,0 +1,72 @@
+import 'dart:ffi';
+
+import 'package:frontend/Model/model/general_list_model.dart';
+import 'package:frontend/Model/model/general_model.dart';
+import 'package:json_annotation/json_annotation.dart';
+
+part 'equipment_list_model.g.dart';
+
+@JsonSerializable()
+class EquipmentResponseModel extends GeneralModel {
+ final EquipmentResponse data;
+
+ EquipmentResponseModel({
+ required super.status,
+ required super.code,
+ required super.message,
+ required this.data,
+ });
+
+ factory EquipmentResponseModel.fromJson(Map json) =>
+ _$EquipmentResponseModelFromJson(json);
+}
+
+@JsonSerializable()
+class EquipmentResponse extends GeneralListModel {
+ List content;
+
+ EquipmentResponse({
+ required super.pageable,
+ required super.last,
+ required super.totalPages,
+ required super.totalElements,
+ required super.size,
+ required super.number,
+ required super.sort,
+ required super.first,
+ required super.numberOfElements,
+ required super.empty,
+ required this.content,
+ });
+
+ factory EquipmentResponse.fromJson(Map json) =>
+ _$EquipmentResponseFromJson(json);
+}
+
+@JsonSerializable()
+class EquipmentModel {
+ String category;
+ String contact;
+ String? description;
+ int equipmentId;
+ String? imgUrl;
+ String keeper;
+ String? location;
+ String name;
+ int quantity;
+
+ EquipmentModel({
+ required this.category,
+ required this.contact,
+ required this.description,
+ required this.equipmentId,
+ required this.imgUrl,
+ required this.keeper,
+ required this.location,
+ required this.name,
+ required this.quantity,
+ });
+
+ factory EquipmentModel.fromJson(Map json) =>
+ _$EquipmentModelFromJson(json);
+}
diff --git a/lib/Model/model/equipment/equipment_list_model.g.dart b/lib/Model/model/equipment/equipment_list_model.g.dart
new file mode 100644
index 0000000..2f8dbd4
--- /dev/null
+++ b/lib/Model/model/equipment/equipment_list_model.g.dart
@@ -0,0 +1,83 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'equipment_list_model.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+EquipmentResponseModel _$EquipmentResponseModelFromJson(
+ Map json) =>
+ EquipmentResponseModel(
+ status: json['status'] as int,
+ code: json['code'] as String,
+ message: json['message'] as String,
+ data: EquipmentResponse.fromJson(json['data'] as Map),
+ );
+
+Map _$EquipmentResponseModelToJson(
+ EquipmentResponseModel instance) =>
+ {
+ 'status': instance.status,
+ 'code': instance.code,
+ 'message': instance.message,
+ 'data': instance.data,
+ };
+
+EquipmentResponse _$EquipmentResponseFromJson(Map json) =>
+ EquipmentResponse(
+ pageable: Pageable.fromJson(json['pageable'] as Map),
+ last: json['last'] as bool,
+ totalPages: json['totalPages'] as int,
+ totalElements: json['totalElements'] as int,
+ size: json['size'] as int,
+ number: json['number'] as int,
+ sort: Sort.fromJson(json['sort'] as Map),
+ first: json['first'] as bool,
+ numberOfElements: json['numberOfElements'] as int,
+ empty: json['empty'] as bool,
+ content: (json['content'] as List)
+ .map((e) => EquipmentModel.fromJson(e as Map))
+ .toList(),
+ );
+
+Map _$EquipmentResponseToJson(EquipmentResponse instance) =>
+ {
+ 'pageable': instance.pageable,
+ 'last': instance.last,
+ 'totalPages': instance.totalPages,
+ 'totalElements': instance.totalElements,
+ 'size': instance.size,
+ 'number': instance.number,
+ 'sort': instance.sort,
+ 'first': instance.first,
+ 'numberOfElements': instance.numberOfElements,
+ 'empty': instance.empty,
+ 'content': instance.content,
+ };
+
+EquipmentModel _$EquipmentModelFromJson(Map json) =>
+ EquipmentModel(
+ category: json['category'] as String,
+ contact: json['contact'] as String,
+ description: json['description'] as String?,
+ equipmentId: json['equipmentId'] as int,
+ imgUrl: json['imgUrl'] as String?,
+ keeper: json['keeper'] as String,
+ location: json['location'] as String?,
+ name: json['name'] as String,
+ quantity: json['quantity'] as int,
+ );
+
+Map _$EquipmentModelToJson(EquipmentModel instance) =>
+ {
+ 'category': instance.category,
+ 'contact': instance.contact,
+ 'description': instance.description,
+ 'equipmentId': instance.equipmentId,
+ 'imgUrl': instance.imgUrl,
+ 'keeper': instance.keeper,
+ 'location': instance.location,
+ 'name': instance.name,
+ 'quantity': instance.quantity,
+ };
diff --git a/lib/Presenter/equipment/equipment_service.dart b/lib/Presenter/equipment/equipment_service.dart
new file mode 100644
index 0000000..4d87cd0
--- /dev/null
+++ b/lib/Presenter/equipment/equipment_service.dart
@@ -0,0 +1,17 @@
+import 'package:frontend/Model/network/api_manager.dart';
+
+class EquipmentService {
+ final equipmentURL = '/equipments?size=100';
+
+ static final EquipmentService _equipmentService = EquipmentService._();
+ EquipmentService._();
+ factory EquipmentService() {
+ return _equipmentService;
+ }
+
+ Future getEquipment() async {
+ final response =
+ APIManager().request(RequestType.get, equipmentURL, null, null, null);
+ return response;
+ }
+}
diff --git a/lib/View/common/screen/root_tab.dart b/lib/View/common/screen/root_tab.dart
index c74721d..5beb5db 100644
--- a/lib/View/common/screen/root_tab.dart
+++ b/lib/View/common/screen/root_tab.dart
@@ -1,19 +1,16 @@
import 'package:flutter/material.dart';
import 'package:frontend/Model/network/api_manager.dart';
import 'package:frontend/View/colors.dart';
+import 'package:frontend/View/equipment/screen/equipment_screen.dart';
import '../../booking/screen/booking_screen.dart';
import '../component/tabbar_item.dart';
import '../../booking/screen/booking_history_screen.dart';
class RootTab extends StatefulWidget {
-
final int? initialIndex;
- const RootTab({
- this.initialIndex,
- Key? key
- }) : super(key: key);
+ const RootTab({this.initialIndex, Key? key}) : super(key: key);
@override
State createState() => _RootTabState();
@@ -38,9 +35,10 @@ class _RootTabState extends State with SingleTickerProviderStateMixin {
super.initState();
controller = TabController(
- length: APIManager().isAdmin ? adminTabBarIcons.length : basicTabBarIcons.length,
- vsync: this
- );
+ length: APIManager().isAdmin
+ ? adminTabBarIcons.length
+ : basicTabBarIcons.length,
+ vsync: this);
controller.addListener(tabListener);
index = widget.initialIndex ?? 0;
selectedTab = widget.initialIndex ?? 0;
@@ -71,15 +69,21 @@ class _RootTabState extends State with SingleTickerProviderStateMixin {
if (APIManager().isAdmin) {
return [
const BookingScreen(),
- const BookingScreen(),
- const BookingHistoryScreen(isAdmin: false,),
- const BookingHistoryScreen(isAdmin: true,),
+ const EquipmentScreen(),
+ const BookingHistoryScreen(
+ isAdmin: false,
+ ),
+ const BookingHistoryScreen(
+ isAdmin: true,
+ ),
];
} else {
return [
const BookingScreen(),
- const BookingScreen(),
- const BookingHistoryScreen(isAdmin: false,),
+ const EquipmentScreen(),
+ const BookingHistoryScreen(
+ isAdmin: false,
+ ),
];
}
}
@@ -87,10 +91,9 @@ class _RootTabState extends State with SingleTickerProviderStateMixin {
/// 화면 중앙 UI 구현 메소드 ///
Widget renderTabBarView() {
return TabBarView(
- physics: const NeverScrollableScrollPhysics(),
- controller: controller,
- children: getTabBarItemList()
- );
+ physics: const NeverScrollableScrollPhysics(),
+ controller: controller,
+ children: getTabBarItemList());
}
/// 하단 탭바 구현 메소드 ///
@@ -101,12 +104,10 @@ class _RootTabState extends State with SingleTickerProviderStateMixin {
backgroundColor: Colors.white,
selectedItemColor: purple,
unselectedItemColor: Colors.black,
-
selectedLabelStyle: renderLabelStyle(),
unselectedLabelStyle: renderLabelStyle(),
type: BottomNavigationBarType.fixed,
-
- onTap: (index){
+ onTap: (index) {
controller.animateTo(index);
setState(() {
selectedTab = 0;
@@ -119,9 +120,6 @@ class _RootTabState extends State with SingleTickerProviderStateMixin {
}
TextStyle renderLabelStyle() {
- return const TextStyle(
- fontSize: 10,
- fontWeight: FontWeight.w500
- );
+ return const TextStyle(fontSize: 10, fontWeight: FontWeight.w500);
}
}
diff --git a/lib/View/equipment/component/equipment_cell.dart b/lib/View/equipment/component/equipment_cell.dart
new file mode 100644
index 0000000..862f3e6
--- /dev/null
+++ b/lib/View/equipment/component/equipment_cell.dart
@@ -0,0 +1,167 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:frontend/Model/model/equipment/equipment_list_model.dart';
+import 'package:frontend/Model/model/notification/notification_response.dart';
+
+class EquipmentCell extends StatefulWidget {
+ final EquipmentModel equipment;
+
+ const EquipmentCell({super.key, required this.equipment});
+
+ @override
+ State createState() => _EquipmentCellState();
+}
+
+class _EquipmentCellState extends State {
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return equipmentCell();
+ }
+
+ Widget equipmentCell() {
+ return Container(
+ width: double.infinity,
+ height: 120,
+ decoration: ShapeDecoration(
+ color: const Color(0xFFF7F3FB),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(10),
+ ),
+ ),
+ margin: const EdgeInsets.only(top: 10),
+ child: Row(
+ children: [
+ Container(
+ margin: const EdgeInsets.only(top: 14, left: 14, bottom: 14),
+ child: (widget.equipment.imgUrl == null)
+ ? Image.asset('asset/image/pladi_icon.png')
+ : Image.network(widget.equipment.imgUrl!,
+ fit: BoxFit.fitWidth)),
+ Container(
+ margin: const EdgeInsets.only(
+ top: 17, left: 10, bottom: 14, right: 10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Container(
+ alignment: Alignment.center,
+ width: 65,
+ height: 30,
+ padding: const EdgeInsets.symmetric(
+ horizontal: 4.72, vertical: 1.18),
+ decoration: ShapeDecoration(
+ color: const Color(0xFF640FAF),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ ),
+ child: Text(
+ widget.equipment.category,
+ textAlign: TextAlign.center,
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 14,
+ fontFamily: 'NanumSquare_ac',
+ fontWeight: FontWeight.w400,
+ height: 0,
+ ),
+ ),
+ ),
+ const SizedBox(
+ width: 10,
+ ),
+ Text(
+ widget.equipment.name,
+ style: const TextStyle(
+ color: Color(0xFF640FAF),
+ fontSize: 15,
+ fontFamily: 'NanumSquare_ac',
+ fontWeight: FontWeight.w700,
+ height: 0.10,
+ letterSpacing: 0.10,
+ ),
+ ),
+ const SizedBox(
+ width: 10,
+ ),
+ Text(
+ widget.equipment.quantity.toString(),
+ style: const TextStyle(
+ color: Color(0xFF717171),
+ fontSize: 13,
+ fontFamily: 'NanumSquare_ac',
+ fontWeight: FontWeight.w700,
+ height: 0.14,
+ letterSpacing: 0.10,
+ ),
+ )
+ ],
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Row(
+ children: [
+ SvgPicture.asset(
+ 'asset/image/mypage_icon.svg',
+ width: 22,
+ height: 22,
+ ),
+ Container(
+ width: 10,
+ ),
+ Text(
+ "${widget.equipment.keeper}(${widget.equipment.contact})",
+ style: const TextStyle(
+ color: Color(0xFF717171),
+ fontSize: 13,
+ fontFamily: 'NanumSquare_ac',
+ fontWeight: FontWeight.w700,
+ height: 0.14,
+ letterSpacing: 0.10,
+ ),
+ )
+ ],
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Row(
+ children: [
+ SvgPicture.asset(
+ 'asset/image/location.svg',
+ width: 22,
+ height: 22,
+ ),
+ Container(
+ width: 10,
+ ),
+ Text(
+ widget.equipment.location == null
+ ? "보관 장소"
+ : widget.equipment.location!,
+ style: const TextStyle(
+ color: Color(0xFF717171),
+ fontSize: 13,
+ fontFamily: 'NanumSquare_ac',
+ fontWeight: FontWeight.w700,
+ height: 0.14,
+ letterSpacing: 0.10,
+ ),
+ )
+ ],
+ ),
+ ],
+ ),
+ )
+ ],
+ ));
+ }
+}
diff --git a/lib/View/equipment/screen/equipment_screen.dart b/lib/View/equipment/screen/equipment_screen.dart
new file mode 100644
index 0000000..6208703
--- /dev/null
+++ b/lib/View/equipment/screen/equipment_screen.dart
@@ -0,0 +1,64 @@
+import 'package:flutter/material.dart';
+import 'package:frontend/Model/model/equipment/equipment_list_model.dart';
+import 'package:frontend/Presenter/equipment/equipment_service.dart';
+import 'package:frontend/View/common/component/main_app_bar.dart';
+import 'package:frontend/View/equipment/component/equipment_cell.dart';
+
+class EquipmentScreen extends StatefulWidget {
+ const EquipmentScreen({super.key});
+
+ @override
+ State createState() => _EquipmentScreen();
+}
+
+class _EquipmentScreen extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(appBar: const MainAppBar(), body: futureBody());
+ }
+
+ Widget futureBody() {
+ List notificationList;
+ return FutureBuilder(
+ future: EquipmentService().getEquipment(),
+ builder: (context, snapshot) {
+ if (snapshot.hasError) {
+ return noDataBody();
+ }
+
+ if (snapshot.hasData) {
+ notificationList =
+ EquipmentResponseModel.fromJson(snapshot.data).data.content;
+ if (notificationList.isEmpty) {
+ return noDataBody();
+ } else {
+ return renderBody(notificationList);
+ }
+ } else {
+ return const CircularProgressIndicator();
+ }
+ });
+ }
+
+ Widget noDataBody() {
+ return Center(
+ child: Container(
+ width: double.infinity,
+ height: double.infinity,
+ alignment: Alignment.center,
+ child: const Text("비품 목록이 없습니다.",
+ style: TextStyle(fontSize: 16, color: Colors.purple)),
+ ));
+ }
+
+ Widget renderBody(List equipmentList) {
+ return ListView.builder(
+ padding: const EdgeInsets.only(left: 20, right: 20, top: 7),
+ scrollDirection: Axis.vertical,
+ itemCount: equipmentList.length,
+ itemBuilder: (BuildContext context, int index) {
+ return EquipmentCell(equipment: equipmentList[index]);
+ },
+ );
+ }
+}