From 6e05ccb7cdd4bedf7be06217056331f310a7899e Mon Sep 17 00:00:00 2001 From: SanghyeonPark Date: Thu, 12 Dec 2024 22:59:03 +0900 Subject: [PATCH 1/2] combine combine back and front - except request --- noto-App/noto-App.xcodeproj/project.pbxproj | 48 + noto-App/noto-App/Api/Delete_Api.swift | 41 + noto-App/noto-App/Api/Get_Api.swift | 40 + noto-App/noto-App/Api/Model_Api.swift | 13 + noto-App/noto-App/Api/Patch_Api.swift | 53 + noto-App/noto-App/Api/Post_Api.swift | 53 + noto-App/noto-App/Api/Put_Api.swift | 87 ++ .../noto-App/Component/Modal_Component.swift | 1079 ++++++++++++++++- .../Project_Progress_Component.swift | 27 +- .../Component/Request_Component.swift | 2 +- .../noto-App/DataModel/Project_Model.swift | 50 + .../noto-App/DataModel/Request_Model.swift | 56 + noto-App/noto-App/DataModel/Todo_Model.swift | 48 + noto-App/noto-App/DataModel/User_Model.swift | 12 + noto-App/noto-App/Page/Main_Page.swift | 278 ++++- .../noto-App/Page/ProgressList_Page.swift | 107 +- noto-App/noto-App/Page/Project_Page.swift | 282 +++-- .../noto-App/Page/RequestDetail_Page.swift | 239 ++-- noto-App/noto-App/Page/RequestList_Page.swift | 183 +-- noto-App/noto-App/Page/Setting_Page.swift | 47 +- noto-App/noto-App/Page/TodoDetail_Page.swift | 234 ++-- 21 files changed, 2472 insertions(+), 507 deletions(-) create mode 100644 noto-App/noto-App/Api/Delete_Api.swift create mode 100644 noto-App/noto-App/Api/Get_Api.swift create mode 100644 noto-App/noto-App/Api/Model_Api.swift create mode 100644 noto-App/noto-App/Api/Patch_Api.swift create mode 100644 noto-App/noto-App/Api/Post_Api.swift create mode 100644 noto-App/noto-App/Api/Put_Api.swift create mode 100644 noto-App/noto-App/DataModel/Project_Model.swift create mode 100644 noto-App/noto-App/DataModel/Request_Model.swift create mode 100644 noto-App/noto-App/DataModel/Todo_Model.swift create mode 100644 noto-App/noto-App/DataModel/User_Model.swift diff --git a/noto-App/noto-App.xcodeproj/project.pbxproj b/noto-App/noto-App.xcodeproj/project.pbxproj index 0a04093..e0e594f 100644 --- a/noto-App/noto-App.xcodeproj/project.pbxproj +++ b/noto-App/noto-App.xcodeproj/project.pbxproj @@ -36,6 +36,16 @@ 62D1EA682CFDA6B1005801F6 /* Project_DataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1EA672CFDA6AC005801F6 /* Project_DataModel.swift */; }; 62D1EA6A2CFDA962005801F6 /* ProgressList_Page.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1EA692CFDA95B005801F6 /* ProgressList_Page.swift */; }; 62FB214A2CFEAE1800B298F0 /* Project_Page.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62FB21492CFEAE0300B298F0 /* Project_Page.swift */; }; + 7D62A1602D02992600939D4B /* Todo_Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A15F2D02990900939D4B /* Todo_Model.swift */; }; + 7D62A16A2D02A09B00939D4B /* Model_Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A1682D02A08A00939D4B /* Model_Api.swift */; }; + 7D62A16D2D02A94E00939D4B /* Get_Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A16B2D02A94400939D4B /* Get_Api.swift */; }; + 7D62A16F2D02D32600939D4B /* Request_Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A16E2D02D30C00939D4B /* Request_Model.swift */; }; + 7D62A1712D02E29100939D4B /* Project_Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A1702D02E27300939D4B /* Project_Model.swift */; }; + 7D62A1772D07827C00939D4B /* Delete_Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A1762D07827300939D4B /* Delete_Api.swift */; }; + 7D62A1792D087AAC00939D4B /* Post_Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A1782D087AA500939D4B /* Post_Api.swift */; }; + 7D62A17B2D0885B700939D4B /* User_Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A17A2D0885B000939D4B /* User_Model.swift */; }; + 7D62A17D2D0889DF00939D4B /* Put_Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A17C2D0889DA00939D4B /* Put_Api.swift */; }; + 7D62A17F2D0B1A9000939D4B /* Patch_Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D62A17E2D0B1A8A00939D4B /* Patch_Api.swift */; }; 7D8261FC2CED8A210045913B /* Modal_Component.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D8261FB2CED8A100045913B /* Modal_Component.swift */; }; /* End PBXBuildFile section */ @@ -90,6 +100,16 @@ 62D1EA672CFDA6AC005801F6 /* Project_DataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project_DataModel.swift; sourceTree = ""; }; 62D1EA692CFDA95B005801F6 /* ProgressList_Page.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressList_Page.swift; sourceTree = ""; }; 62FB21492CFEAE0300B298F0 /* Project_Page.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project_Page.swift; sourceTree = ""; }; + 7D62A15F2D02990900939D4B /* Todo_Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Todo_Model.swift; sourceTree = ""; }; + 7D62A1682D02A08A00939D4B /* Model_Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model_Api.swift; sourceTree = ""; }; + 7D62A16B2D02A94400939D4B /* Get_Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Get_Api.swift; sourceTree = ""; }; + 7D62A16E2D02D30C00939D4B /* Request_Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Request_Model.swift; sourceTree = ""; }; + 7D62A1702D02E27300939D4B /* Project_Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Project_Model.swift; sourceTree = ""; }; + 7D62A1762D07827300939D4B /* Delete_Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delete_Api.swift; sourceTree = ""; }; + 7D62A1782D087AA500939D4B /* Post_Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post_Api.swift; sourceTree = ""; }; + 7D62A17A2D0885B000939D4B /* User_Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User_Model.swift; sourceTree = ""; }; + 7D62A17C2D0889DA00939D4B /* Put_Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Put_Api.swift; sourceTree = ""; }; + 7D62A17E2D0B1A8A00939D4B /* Patch_Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Patch_Api.swift; sourceTree = ""; }; 7D8261FB2CED8A100045913B /* Modal_Component.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal_Component.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -145,6 +165,7 @@ 1371F01A2CDDE0150034FC36 /* noto-App */ = { isa = PBXGroup; children = ( + 7D62A1732D03045000939D4B /* Api */, 62D1EA5D2CFD80D8005801F6 /* DataModel */, 621B9BCD2CF4448F00451367 /* Page */, 627FFE9A2CEC293800DF70E8 /* Component */, @@ -226,6 +247,10 @@ 62D1EA5D2CFD80D8005801F6 /* DataModel */ = { isa = PBXGroup; children = ( + 7D62A17A2D0885B000939D4B /* User_Model.swift */, + 7D62A1702D02E27300939D4B /* Project_Model.swift */, + 7D62A16E2D02D30C00939D4B /* Request_Model.swift */, + 7D62A15F2D02990900939D4B /* Todo_Model.swift */, 62251AA32D0090F5004E1453 /* User_DataModel.swift */, 62D1EA672CFDA6AC005801F6 /* Project_DataModel.swift */, 62D1EA652CFD9775005801F6 /* Person_DataModel.swift */, @@ -235,6 +260,19 @@ path = DataModel; sourceTree = ""; }; + 7D62A1732D03045000939D4B /* Api */ = { + isa = PBXGroup; + children = ( + 7D62A17E2D0B1A8A00939D4B /* Patch_Api.swift */, + 7D62A17C2D0889DA00939D4B /* Put_Api.swift */, + 7D62A1782D087AA500939D4B /* Post_Api.swift */, + 7D62A16B2D02A94400939D4B /* Get_Api.swift */, + 7D62A1762D07827300939D4B /* Delete_Api.swift */, + 7D62A1682D02A08A00939D4B /* Model_Api.swift */, + ); + path = Api; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -369,28 +407,38 @@ buildActionMask = 2147483647; files = ( 1371F01E2CDDE0150034FC36 /* ContentView.swift in Sources */, + 7D62A16D2D02A94E00939D4B /* Get_Api.swift in Sources */, 621B9BD72CF46B5800451367 /* Main_Page.swift in Sources */, 627FFE9C2CEC295100DF70E8 /* FontStyle.swift in Sources */, 62251AA22D008827004E1453 /* RequestDetail_Page.swift in Sources */, 62D1EA612CFD813D005801F6 /* Todo_DataModel.swift in Sources */, 621B9BD92CF46C5100451367 /* Project_Progress_Component.swift in Sources */, 62D1EA632CFD8865005801F6 /* TodoDetail_Page.swift in Sources */, + 7D62A1792D087AAC00939D4B /* Post_Api.swift in Sources */, 7D8261FC2CED8A210045913B /* Modal_Component.swift in Sources */, + 7D62A1602D02992600939D4B /* Todo_Model.swift in Sources */, + 7D62A1772D07827C00939D4B /* Delete_Api.swift in Sources */, 62D1EA662CFD9779005801F6 /* Person_DataModel.swift in Sources */, + 7D62A1712D02E29100939D4B /* Project_Model.swift in Sources */, 627FFE9E2CEC299700DF70E8 /* Frame_Component.swift in Sources */, + 7D62A17F2D0B1A9000939D4B /* Patch_Api.swift in Sources */, 1371F0202CDDE0150034FC36 /* Item.swift in Sources */, 62FB214A2CFEAE1800B298F0 /* Project_Page.swift in Sources */, 621B9BDD2CF47F2C00451367 /* RequestList_Page.swift in Sources */, 62D1EA6A2CFDA962005801F6 /* ProgressList_Page.swift in Sources */, 62251AA42D009101004E1453 /* User_DataModel.swift in Sources */, + 7D62A17D2D0889DF00939D4B /* Put_Api.swift in Sources */, 62D1EA5F2CFD80F6005801F6 /* Request_DataModel.swift in Sources */, 62D1EA682CFDA6B1005801F6 /* Project_DataModel.swift in Sources */, 1371F01C2CDDE0150034FC36 /* noto_AppApp.swift in Sources */, 621B9BD52CF46B3B00451367 /* Sub_Component.swift in Sources */, + 7D62A17B2D0885B700939D4B /* User_Model.swift in Sources */, 621B9BC92CF4440800451367 /* Request_Component.swift in Sources */, + 7D62A16A2D02A09B00939D4B /* Model_Api.swift in Sources */, 621B9BC72CF443D600451367 /* Navigation_Componenet.swift in Sources */, 627078DE2CE30A660027FF09 /* ColorStyle.swift in Sources */, 621B9BC52CF4438800451367 /* Header_Component.swift in Sources */, + 7D62A16F2D02D32600939D4B /* Request_Model.swift in Sources */, 621B9BCB2CF4442C00451367 /* SearchBar_Component.swift in Sources */, 621B9BDB2CF4750400451367 /* Setting_Page.swift in Sources */, ); diff --git a/noto-App/noto-App/Api/Delete_Api.swift b/noto-App/noto-App/Api/Delete_Api.swift new file mode 100644 index 0000000..763eb7b --- /dev/null +++ b/noto-App/noto-App/Api/Delete_Api.swift @@ -0,0 +1,41 @@ +// +// Delete_Api.swift +// noto-App +// +// Created by 박상현 on 12/10/24. +// +import SwiftUI + +// DELETE 요청을 처리하는 함수 +func delete(url: String) async throws -> ApiModel { + // URL을 유효한 형식으로 만들기 + guard let url = URL(string: url) else { + throw NSError(domain: "Invalid URL", code: 0, userInfo: nil) // URL이 유효하지 않으면 에러 던지기 + } + + // URLRequest 객체 생성 + var request = URLRequest(url: url) + request.httpMethod = "DELETE" // DELETE 요청 메소드 설정 + + // 비동기적으로 DELETE 요청 보내기 + let (data, _) = try await URLSession.shared.data(for: request) + + // 응답 데이터 출력 (디버깅용) + if let jsonString = String(data: data, encoding: .utf8) { + print("Raw JSON Response: \(jsonString)") + } + + // JSONDecoder 생성 + let decoder = JSONDecoder() + + // Custom DateFormatter를 사용하여 특정 날짜 형식 처리 + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" // 필요한 날짜 형식으로 설정 + + // 커스텀 포맷터를 디코더에 설정 + decoder.dateDecodingStrategy = .formatted(formatter) + + // API 응답을 지정된 모델로 디코딩 + let apiResponse = try decoder.decode(ApiModel.self, from: data) + return apiResponse +} diff --git a/noto-App/noto-App/Api/Get_Api.swift b/noto-App/noto-App/Api/Get_Api.swift new file mode 100644 index 0000000..1727308 --- /dev/null +++ b/noto-App/noto-App/Api/Get_Api.swift @@ -0,0 +1,40 @@ +// +// Get_Api.swift +// noto-App +// +// Created by 박상현 on 12/6/24. +// +import SwiftUI + +// GET 요청을 처리하는 함수 +func get(url: String, responseType: T.Type) async throws -> ApiModel { + // Try to create a URL from the string + guard let url = URL(string: url) else { + throw NSError(domain: "Invalid URL", code: 0, userInfo: nil) // Throw error if URL is invalid + } + + // Now `url` is of type URL, so we can proceed with the network request + let (data, _) = try await URLSession.shared.data(from: url) + + // Print the raw data for debugging + if let jsonString = String(data: data, encoding: .utf8) { + print("Raw JSON Response: \(jsonString)") + } + + // Create a JSON decoder + let decoder = JSONDecoder() + + // Custom DateFormatter for the expected date format + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" // Specify your custom date format here + + // Set the custom formatter to the decoder + decoder.dateDecodingStrategy = .formatted(formatter) + + // Decode the API response + let apiResponse = try decoder.decode(ApiModel.self, from: data) + return apiResponse +} + + + diff --git a/noto-App/noto-App/Api/Model_Api.swift b/noto-App/noto-App/Api/Model_Api.swift new file mode 100644 index 0000000..698b145 --- /dev/null +++ b/noto-App/noto-App/Api/Model_Api.swift @@ -0,0 +1,13 @@ +// +// Model_Api.swift +// noto-App +// +// Created by 박상현 on 12/6/24. +// +import SwiftUI + +struct ApiModel: Codable { + let code: String + let message: String + let data: T +} diff --git a/noto-App/noto-App/Api/Patch_Api.swift b/noto-App/noto-App/Api/Patch_Api.swift new file mode 100644 index 0000000..9161c11 --- /dev/null +++ b/noto-App/noto-App/Api/Patch_Api.swift @@ -0,0 +1,53 @@ +// +// Patch_Api.swift +// noto-App +// +// Created by 박상현 on 12/12/24. +// +import SwiftUI + +// PATCH 요청을 처리하는 함수 +func patch(url: String, body: U) async throws -> ApiModel { + // Try to create a URL from the string + guard let url = URL(string: url) else { + throw NSError(domain: "Invalid URL", code: 0, userInfo: nil) // Throw error if URL is invalid + } + + // Create the URLRequest for the PUT request + var request = URLRequest(url: url) + request.httpMethod = "PATCH" // HTTP method 변경 (PUT) + + // Set the request's content-type header (application/json for JSON data) + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + // Encode the body into JSON + let encoder = JSONEncoder() + + // 날짜 포맷 설정 + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" // 서버에서 요구하는 날짜 형식으로 설정 + + // JSONEncoder에 날짜 인코딩 전략 설정 + encoder.dateEncodingStrategy = .formatted(formatter) + + let bodyData = try encoder.encode(body) + request.httpBody = bodyData + + // Perform the network request and get the response data + let (data, _) = try await URLSession.shared.data(for: request) + + // Print the raw data for debugging + if let jsonString = String(data: data, encoding: .utf8) { + print("Raw JSON Response: \(jsonString)") + } + + // Create a JSON decoder + let decoder = JSONDecoder() + + // Set the custom formatter to the decoder + decoder.dateDecodingStrategy = .formatted(formatter) + + // Decode the API response + let apiResponse = try decoder.decode(ApiModel.self, from: data) + return apiResponse +} diff --git a/noto-App/noto-App/Api/Post_Api.swift b/noto-App/noto-App/Api/Post_Api.swift new file mode 100644 index 0000000..7cb21bd --- /dev/null +++ b/noto-App/noto-App/Api/Post_Api.swift @@ -0,0 +1,53 @@ +// +// Post_Api.swift +// noto-App +// +// Created by 박상현 on 12/10/24. +// +import SwiftUI + +// POST 요청을 처리하는 함수 +func post(url: String, body: U, responseType: T.Type) async throws -> ApiModel { + // Try to create a URL from the string + guard let url = URL(string: url) else { + throw NSError(domain: "Invalid URL", code: 0, userInfo: nil) // Throw error if URL is invalid + } + + // Create the URLRequest for the POST request + var request = URLRequest(url: url) + request.httpMethod = "POST" + + // Set the request's content-type header (application/json for JSON data) + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + // Encode the body into JSON + let encoder = JSONEncoder() + + // 날짜 포맷 설정 + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" // 서버에서 요구하는 날짜 형식으로 설정 + + // JSONEncoder에 날짜 인코딩 전략 설정 + encoder.dateEncodingStrategy = .formatted(formatter) + + let bodyData = try encoder.encode(body) + request.httpBody = bodyData + + // Perform the network request and get the response data + let (data, _) = try await URLSession.shared.data(for: request) + + // Print the raw data for debugging + if let jsonString = String(data: data, encoding: .utf8) { + print("Raw JSON Response: \(jsonString)") + } + + // Create a JSON decoder + let decoder = JSONDecoder() + + // Set the custom formatter to the decoder + decoder.dateDecodingStrategy = .formatted(formatter) + + // Decode the API response + let apiResponse = try decoder.decode(ApiModel.self, from: data) + return apiResponse +} diff --git a/noto-App/noto-App/Api/Put_Api.swift b/noto-App/noto-App/Api/Put_Api.swift new file mode 100644 index 0000000..a18edb3 --- /dev/null +++ b/noto-App/noto-App/Api/Put_Api.swift @@ -0,0 +1,87 @@ +// +// Put_Api.swift +// noto-App +// +// Created by 박상현 on 12/10/24. +// +import SwiftUI + +// PUT 요청을 처리하는 함수 +func put(url: String, body: U, responseType: T.Type) async throws -> ApiModel { + // Try to create a URL from the string + guard let url = URL(string: url) else { + throw NSError(domain: "Invalid URL", code: 0, userInfo: nil) // Throw error if URL is invalid + } + + // Create the URLRequest for the PUT request + var request = URLRequest(url: url) + request.httpMethod = "PUT" // HTTP method 변경 (PUT) + + // Set the request's content-type header (application/json for JSON data) + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + // Encode the body into JSON + let encoder = JSONEncoder() + + // 날짜 포맷 설정 + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" // 서버에서 요구하는 날짜 형식으로 설정 + + // JSONEncoder에 날짜 인코딩 전략 설정 + encoder.dateEncodingStrategy = .formatted(formatter) + + let bodyData = try encoder.encode(body) + request.httpBody = bodyData + + // Perform the network request and get the response data + let (data, _) = try await URLSession.shared.data(for: request) + + // Print the raw data for debugging + if let jsonString = String(data: data, encoding: .utf8) { + print("Raw JSON Response: \(jsonString)") + } + + // Create a JSON decoder + let decoder = JSONDecoder() + + // Set the custom formatter to the decoder + decoder.dateDecodingStrategy = .formatted(formatter) + + // Decode the API response + let apiResponse = try decoder.decode(ApiModel.self, from: data) + return apiResponse +} + +// PUT none body 요청을 처리하는 함수 +func putNoneBody(url: String) async throws -> ApiModel { + // Try to create a URL from the string + guard let url = URL(string: url) else { + throw NSError(domain: "Invalid URL", code: 0, userInfo: nil) // Throw error if URL is invalid + } + + // Create the URLRequest for the PUT request + var request = URLRequest(url: url) + request.httpMethod = "PUT" // HTTP method 변경 (PUT) + + // Perform the network request and get the response data + let (data, _) = try await URLSession.shared.data(for: request) + + // Print the raw data for debugging + if let jsonString = String(data: data, encoding: .utf8) { + print("Raw JSON Response: \(jsonString)") + } + + // JSONDecoder 생성 + let decoder = JSONDecoder() + + // Custom DateFormatter를 사용하여 특정 날짜 형식 처리 + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" // 필요한 날짜 형식으로 설정 + + // 커스텀 포맷터를 디코더에 설정 + decoder.dateDecodingStrategy = .formatted(formatter) + + // API 응답을 지정된 모델로 디코딩 + let apiResponse = try decoder.decode(ApiModel.self, from: data) + return apiResponse +} diff --git a/noto-App/noto-App/Component/Modal_Component.swift b/noto-App/noto-App/Component/Modal_Component.swift index f8fcf4e..b263851 100644 --- a/noto-App/noto-App/Component/Modal_Component.swift +++ b/noto-App/noto-App/Component/Modal_Component.swift @@ -6,30 +6,6 @@ // import SwiftUI -// Add 행 컴포넌트 -struct rowAddComponent: View { - var action: (String) -> Void - - var body: some View { - Button(action: { - action("Row component clicked") - }) { - HStack { - Image("user") - .resizable() - .frame(width: 24, height: 24) - .padding(.trailing, 5) - VStack(alignment: .leading) { - Text("추가") - .subTitleFont() - } - } - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.leading, 20) - } - } -} - // 레퍼런스 모달 컴포넌트 struct modalReferenceView: View { var body: some View { @@ -48,17 +24,217 @@ struct modalReferenceView: View { } // 일정 리스트 모달 컴포넌트 -struct modalScheduleListView: View { +struct modalTodoListView: View { + @Environment(\.dismiss) var dismiss + @State private var showingSheet: Bool = false + @State private var showingDialog: Bool = false + @State private var deletedSid: Int = 0 + @State private var modifiedSid: Int = 0 + @Binding var scheduleProjectData: [ScheduleProject]? + @Binding var currentScreen: Page + @Binding var selectedPid: Int + @Binding var selectedSid: Int + var body: some View { VStack { - /*rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test)*/ - + if let scheduleProjectData = scheduleProjectData { + ForEach(scheduleProjectData, id: \.self) { schedule in + HStack { + rowComponent( + imageName: "user", + title: schedule.name, + subtitle: schedule.description, + action: { + dismiss() + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { + currentScreen = .todoDetail + selectedSid = schedule.id + } + } + ) + Button(action: { + showingDialog = true + deletedSid = schedule.id + modifiedSid = schedule.id + }) { + Image(systemName: "ellipsis") + .font(.system(size: 24)) + .foregroundColor(.customBlack) + } + .padding(.trailing) + .confirmationDialog("Choose an option", isPresented: $showingDialog) { + Button("일정 수정", role: .none) { + print("일정 수정") + showingSheet = true + } + .sheet(isPresented: $showingSheet, onDismiss: { + showingSheet = false + }){ + modalTodoView(modifiedSid: $modifiedSid, selectedPid: $selectedPid) + } + Button("일정 삭제", role: .destructive) { + Task { + do { + // 일정 삭제 호출 + let response = try await delete(url: "https://www.bestbirthday.co.kr:8080/api/schedule" + "/\(deletedSid)") + print(response) + print("일정 삭제", deletedSid) + } catch { + print("일정 삭제 실패: \(error.localizedDescription)") + } + } + } + Button("취소", role: .cancel) { + print("취소") + } + } + } + } + } + Divider() + Button(action: { + showingSheet = true + modifiedSid = 0 + }) { + HStack { + Image("user") + .resizable() + .frame(width: 24, height: 24) + .padding(.trailing, 5) + VStack(alignment: .leading) { + Text("생성") + .titleFont() + .lineLimit(1) + .truncationMode(.tail) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 20) + } + .sheet(isPresented: $showingSheet, onDismiss: { + showingSheet = false + }){ + modalTodoView(modifiedSid: $modifiedSid, selectedPid: $selectedPid) + } + } + .modalPresentation() + } +} + +// 일정 리스트 모달 컴포넌트 +struct modalTodayTodoListView: View { + @Environment(\.dismiss) var dismiss + @Binding var scheduleTodayData: ScheduleToday? + @Binding var currentScreen: Page + @Binding var selectedSid: Int + + var body: some View { + VStack { + if let scheduleTodayData = scheduleTodayData { + ForEach(scheduleTodayData.todaySchedule, id: \.self) { schedule in + HStack { + rowComponent( + imageName: "user", + title: schedule.name, + subtitle: schedule.description, + action: { + dismiss() + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { + currentScreen = .todoDetail + selectedSid = schedule.id + } + } + ) + } + } + } + } + .modalPresentation() + } +} + +// 일정 리스트 모달 컴포넌트 +struct modalProjectTodoListView: View { + @Environment(\.dismiss) var dismiss + let scheduleProjectData: [ScheduleProject]? + @Binding var currentScreen: Page + @Binding var selectedSid: Int + + var body: some View { + VStack { + if let scheduleProjectData = scheduleProjectData { + ForEach(scheduleProjectData, id: \.self) { schedule in + HStack { + rowComponent( + imageName: "user", + title: schedule.name, + subtitle: schedule.description, + action: { + dismiss() + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { + currentScreen = .todoDetail + selectedSid = schedule.id + } + } + ) + } + } + } + } + .modalPresentation() + } +} + + +// 리퀘스트 리스트 모달 컴포넌트 +struct modalRequestListView: View { + @Environment(\.dismiss) var dismiss + @Binding var requestListData: RequestList? + @Binding var selectedModal: Modal + @Binding var currentScreen: Page + @Binding var selectedRid: Int + + var body: some View { + VStack { + if let requestListData = requestListData { + if selectedModal == .receiveRequestList{ + ForEach(requestListData.receiveRequest, id: \.self) { receiveRequest in + HStack { + rowComponent(imageName: "mail", + title: receiveRequest.title, + subtitle: receiveRequest.sender, + action: { + dismiss() + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { + currentScreen = .requestDetail + selectedRid = receiveRequest.requestId + } + }) + } + } + } + else if selectedModal == .sendRequestList{ + ForEach(requestListData.sendRequest, id: \.self) { sendRequest in + HStack { + rowComponent(imageName: "mail", + title: sendRequest.title, + subtitle: sendRequest.sender, + action: { + dismiss() + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { + currentScreen = .requestDetail + selectedRid = sendRequest.requestId + } + }) + } + } + } + } } .modalPresentation() } @@ -66,46 +242,840 @@ struct modalScheduleListView: View { // 프로젝트 리스트 모달 컴포넌트 struct modalProjectListView: View { + @Environment(\.dismiss) var dismiss + @State private var showingSheet: Bool = false + @State private var showingDialog: Bool = false + @State private var deletedPid: Int = 0 + @State private var modifiedPid: Int = 0 + @Binding var projectListData: ProjectList? + @Binding var currentScreen: Page + @Binding var selectedPid: Int + var body: some View { - VStack(spacing: 20) { - VStack(spacing: 10) { - /*rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test) - rowComponent(imageName: imageName, title: title, subtitle: subtitle, action: test)*/ - Divider() - //rowAddComponent(action: test) - } - .frame(maxWidth: .infinity, alignment: .center) - .padding(.top) - .padding(.bottom) - .blockStyle(height: .infinity) + VStack { + if let projectListData = projectListData { + ForEach(projectListData.projects, id: \.self) { projects in + HStack { + rowComponent( + imageName: "user", + title: projects.name, + subtitle: projects.description, + action: { + dismiss() + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { + currentScreen = .projectDetail + selectedPid = projects.projectId + } + } + ) + Button(action: { + showingDialog = true + deletedPid = projects.projectId + modifiedPid = projects.projectId + }) { + Image(systemName: "ellipsis") + .font(.system(size: 24)) + .foregroundColor(.customBlack) + } + .padding(.trailing) + .confirmationDialog("Choose an option", isPresented: $showingDialog) { + Button("프로젝트 수정", role: .none) { + print("프로젝트 수정") + showingSheet = true + } + .sheet(isPresented: $showingSheet, onDismiss: { + showingSheet = false + }){ + modalProjectView(modifiedPid: $modifiedPid) + } + Button("프로젝트 삭제", role: .destructive) { + Task { + do { + // 프로젝트 삭제 호출 + let response = try await delete(url: "https://www.bestbirthday.co.kr:8080/api/project" + "/\(deletedPid)") + print(response) + print("프로젝트 삭제", deletedPid) + } catch { + print("프로젝트 삭제 실패: \(error.localizedDescription)") + } + } + } + Button("취소", role: .cancel) { + print("취소") + } + } + } + } + } + Divider() + Button(action: { + showingSheet = true + modifiedPid = 0 + }) { + HStack { + Image("user") + .resizable() + .frame(width: 24, height: 24) + .padding(.trailing, 5) + VStack(alignment: .leading) { + Text("생성") + .titleFont() + .lineLimit(1) + .truncationMode(.tail) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 20) + } + .sheet(isPresented: $showingSheet, onDismiss: { + showingSheet = false + }){ + modalProjectView(modifiedPid: $modifiedPid) + } } .modalPresentation() + .task { + do { + let projectListResponse = try await get(url: "https://www.bestbirthday.co.kr:8080/api/project/list?userId=1", responseType: ProjectList.self) + // Handle the response here +// print(projectListResponse) + + projectListData = projectListResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } } } // 일정 모달 컴포넌트 -struct modalScheduleView: View { - var action: (String) -> Void +struct modalTodoView: View { + @Environment(\.dismiss) var dismiss + @State private var projectData: Project? = nil + @State private var scheduleInfoData: ScheduleInfo? = nil + @State private var name: String = "" + @State private var startDate: Date = Date() + @State private var endDate: Date = Date() + @State private var participants: [String] = [] + @State private var description: String = "" + @State private var tag: String = "" + @State private var url: String = "" + @State private var isParticipantsListVisible: Bool = false // 추가된 상태 변수 + + @Binding var modifiedSid: Int + @Binding var selectedPid: Int + + // 완료 버튼 활성화 조건 + private var isCompleteButtonEnabled: Bool { + !name.isEmpty && endDate >= startDate && !participants.isEmpty && !tag.isEmpty + } var body: some View { VStack(spacing: 20) { HStack { - Text("일정 생성") + if modifiedSid == 0 { + Text("일정 생성") + .titleFont() + .frame(maxWidth: .infinity, alignment: .leading) + } else { + Text("일정 수정") + .titleFont() + .frame(maxWidth: .infinity, alignment: .leading) + } + + Button(action: { + Task { + do { + if modifiedSid == 0 { + let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/schedule", body: RequestSchedule(projectId: selectedPid, name: name, startDate: startDate, endDate: endDate, participants: participants, description: description, tag: tag, url: url), responseType: ScheduleInfo.self) + print(response) + } else { + let response = try await put(url: "https://www.bestbirthday.co.kr:8080/api/schedule" + "/\(modifiedSid)", body: RequestSchedule(projectId: selectedPid, name: name, startDate: startDate, endDate: endDate, participants: participants, description: description, tag: tag, url: url), responseType: ScheduleInfo.self) + print(response) + } + + dismiss() + } catch { + if modifiedSid == 0 { + print("일정 생성 실패: \(error.localizedDescription)") + } else { + print("일정 수정 실패: \(error.localizedDescription)") + } + } + } + }) { + Text("완료") + .frame(maxWidth: .infinity, alignment: .trailing) + .foregroundColor(isCompleteButtonEnabled ? .blue : .gray) // 버튼 색상 변경 + } + .disabled(!isCompleteButtonEnabled) // 활성화 조건에 따라 버튼 활성화/비활성화 + } + .padding(.leading) + .padding(.trailing) + Divider() + + ZStack(alignment: .topLeading) { + // TextEditor + TextEditor(text: $name) + .padding(10) // TextEditor 내부 여백 + .frame(maxWidth: 350, maxHeight: 50) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + + // Placeholder + if name.isEmpty { + Text("일정 이름") + .foregroundColor(.customLightGray) + .padding(.top, 10) + .padding(.leading, 11) + } + } + + DatePicker( + "시작일 선택", + selection: $startDate, + displayedComponents: [.date] // 날짜만 표시 + ) + .datePickerStyle(.compact) + .frame(maxWidth: 350) + + DatePicker( + "종료일 선택", + selection: $endDate, + displayedComponents: [.date] // 날짜만 표시 + ) + .datePickerStyle(.compact) + .frame(maxWidth: 350) + + // 참가자 선택 버튼 + Button(action: { + isParticipantsListVisible.toggle() // 버튼 클릭 시 리스트 토글 + }) { + Text("참가자 선택") + .foregroundColor(.black) + .frame(maxWidth: 100, maxHeight: 40) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + } + + // 참가자 목록이 표시되는 부분 + if isParticipantsListVisible { + VStack { + if let availableParticipants = projectData?.participants { + List(availableParticipants, id: \.self) { participant in + HStack { + Text(participant) + Spacer() + ZStack { + if participants.contains(participant) { + Image(systemName: "checkmark.square.fill") + .font(.largeTitle) + } else { + Image(systemName: "square") + .font(.largeTitle) + } + }.foregroundColor(.gray) + .onTapGesture { + if participants.contains(participant) { + participants = participants.filter { + $0 != participant + } + } else { + participants.append(participant) + } + } + } + } + } + } + .frame(maxHeight: 200) // List의 높이 제한 + .background(Color.white) // 배경색 + .cornerRadius(8) + .shadow(radius: 5) // 그림자 효과 + .padding(.top) + } + + // Tag 선택 Picker + Picker("태그 선택", selection: $tag) { + Text("회의").tag("회의") + Text("개발").tag("개발") + } + .pickerStyle(SegmentedPickerStyle()) + .frame(maxWidth: 350) + + ZStack(alignment: .topLeading) { + // TextEditor + TextEditor(text: $description) + .padding(10) // TextEditor 내부 여백 + .frame(maxWidth: 350, maxHeight: 200) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + + // Placeholder + if description.isEmpty { + Text("일정 설명") + .foregroundColor(.customLightGray) + .padding(.top, 20) + .padding(.leading, 11) + } + } + } + .modalPresentation() + .task { + if modifiedSid != 0 { + do { + let scheduleInfoResponse = try await get(url: "https://www.bestbirthday.co.kr:8080/api/schedule/info" + "/\(modifiedSid)", responseType: ScheduleInfo.self) + // Handle the response here + print(scheduleInfoResponse) + + scheduleInfoData = scheduleInfoResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + if let scheduleInfoData = scheduleInfoData { + name = scheduleInfoData.name + startDate = scheduleInfoData.startDate + endDate = scheduleInfoData.endDate + participants = scheduleInfoData.participants + description = scheduleInfoData.description + tag = scheduleInfoData.tag + url = scheduleInfoData.url + } + } + } + .task { + do { + let projectResponse = try await get(url: "https://www.bestbirthday.co.kr:8080/api/project" + "/\(selectedPid)", responseType: Project.self) + projectData = projectResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + } +} + +// 프로젝트 모달 컴포넌트 +struct modalProjectView: View { + @Environment(\.dismiss) var dismiss + @State private var name: String = "" + @State private var startDate: Date = Date() + @State private var endDate: Date = Date() + @State private var description: String = "" + @State private var image: String = "" + @State private var projectData: Project? = nil + + @Binding var modifiedPid: Int + + // 완료 버튼 활성화 조건 + private var isCompleteButtonEnabled: Bool { + !name.isEmpty && endDate >= startDate + } + + var body: some View { + VStack(spacing: 20) { + HStack { + if modifiedPid == 0 { + Text("프로젝트 생성") + .titleFont() + .frame(maxWidth: .infinity, alignment: .leading) + } else { + Text("프로젝트 수정") + .titleFont() + .frame(maxWidth: .infinity, alignment: .leading) + } + + Button(action: { + Task { + do { + if modifiedPid == 0 { + let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/project", body: RequestProject(name: name, startDate: startDate, endDate: endDate, description: description, image: image), responseType: Project.self) + print(response) + projectData = response.data + + Task { + do { + if let projectData = projectData { + let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/user/participate", body: RequestUserParticipate(userId: 1, projectId: projectData.id), responseType: String?.self) + + print(response) + } + } catch { + print("유저 참가 실패: \(error.localizedDescription)") + } + } + } else { + let response = try await put(url: "https://www.bestbirthday.co.kr:8080/api/project" + "/\(modifiedPid)", body: RequestProject(name: name, startDate: startDate, endDate: endDate, description: description, image: image), responseType: Project.self) + print(response) + projectData = response.data + } + + dismiss() + } catch { + if modifiedPid == 0 { + print("프로젝트 생성 실패: \(error.localizedDescription)") + } else { + print("프로젝트 수정 실패: \(error.localizedDescription)") + } + } + } + }) { + Text("완료") + .frame(maxWidth: .infinity, alignment: .trailing) + .foregroundColor(isCompleteButtonEnabled ? .blue : .gray) // 버튼 색상 변경 + } + .disabled(!isCompleteButtonEnabled) // 활성화 조건에 따라 버튼 활성화/비활성화 + } + .padding(.leading) + .padding(.trailing) + Divider() + + ZStack(alignment: .topLeading) { + // TextEditor + TextEditor(text: $name) + .padding(10) // TextEditor 내부 여백 + .frame(maxWidth: 350, maxHeight: 50) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + + // Placeholder + if name.isEmpty { + Text("프로젝트 이름") + .foregroundColor(.customLightGray) + .padding(.top, 10) + .padding(.leading, 11) + } + } + + DatePicker( + "시작일 선택", + selection: $startDate, + displayedComponents: [.date] // 날짜만 표시 + ) + .datePickerStyle(.compact) + .frame(maxWidth: 350) + + DatePicker( + "종료일 선택", + selection: $endDate, + displayedComponents: [.date] // 날짜만 표시 + ) + .datePickerStyle(.compact) + .frame(maxWidth: 350) + + ZStack(alignment: .topLeading) { + // TextEditor + TextEditor(text: $description) + .padding(10) // TextEditor 내부 여백 + .frame(maxWidth: 350, maxHeight: 200) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + + // Placeholder + if description.isEmpty { + Text("프로젝트 설명") + .foregroundColor(.customLightGray) + .padding(.top, 20) + .padding(.leading, 11) + } + } + } + .modalPresentation() + .task { + if modifiedPid != 0 { + do { + let projectResponse = try await get(url: "https://www.bestbirthday.co.kr:8080/api/project" + "/\(modifiedPid)", responseType: Project.self) + // Handle the response here + print(projectResponse) + + projectData = projectResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + if let projectData = projectData { + name = projectData.name + startDate = projectData.startDate + endDate = projectData.endDate + description = projectData.description + } + } + } + } +} + +// 리퀘스트 모달 컴포넌트 +struct modalRequestView: View { + @Environment(\.dismiss) var dismiss + @State private var projectListData: ProjectList? = nil + @State private var projectData: Project? = nil + @State private var scheduleInfoData: ScheduleInfo? = nil + @State private var scheduleProjectData: [ScheduleProject]? + @State private var isProjectsListVisible: Bool = false // 추가된 상태 변수 + @State private var isScheduleListVisible: Bool = false // 추가된 상태 변수 + @State private var isParticipantsListVisible: Bool = false // 추가된 상태 변수 + + @State private var projectId: Int = 0 + @State private var scheduleId: Int = 0 + @State private var receivers: [String] = [] + @State private var sender: Int = 1 + @State private var title: String = "" + @State private var content: String = "" + + // 완료 버튼 활성화 조건 + private var isCompleteButtonEnabled: Bool { + projectId != 0 && scheduleId != 0 && !receivers.isEmpty && !title.isEmpty + } + + var body: some View { + VStack(spacing: 20) { + HStack { + Text("리퀘스트 전송") .titleFont() .frame(maxWidth: .infinity, alignment: .leading) + Button(action: { - action("modalScheduleView complete component clicked") + Task { + do { +// let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/request" + "?projectId=\(projectId)" + "&scheduleId=\(scheduleId)" + "&receivers=string&sender=0&title=string&content=string", body: RequestSchedule(projectId: selectedPid, name: name, startDate: startDate, endDate: endDate, participants: participants, description: description, tag: tag, url: url), responseType: ScheduleInfo.self) +// print(response) + + dismiss() + } catch { + print("리퀘스트 전송 실패: \(error.localizedDescription)") + } + } }) { - Text("완료") + Text("전송") .frame(maxWidth: .infinity, alignment: .trailing) + .foregroundColor(isCompleteButtonEnabled ? .blue : .gray) // 버튼 색상 변경 } + .disabled(!isCompleteButtonEnabled) // 활성화 조건에 따라 버튼 활성화/비활성화 } + .padding(.leading) + .padding(.trailing) Divider() + + // 프로젝트 선택 버튼 + Button(action: { + isProjectsListVisible.toggle() // 버튼 클릭 시 리스트 토글 + }) { + Text("프로젝트 선택") + .foregroundColor(.black) + .frame(maxWidth: 100, maxHeight: 40) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + } + + // 프로젝트 목록이 표시되는 부분 + if isProjectsListVisible { + VStack { + if let availableProjects = projectListData?.projects { + List(availableProjects, id: \.self) { project in + HStack { + Text(project.name) + Spacer() + ZStack { + if projectId == project.projectId { // 선택된 프로젝트가 있는지 확인 + Image(systemName: "checkmark.square.fill") + .font(.largeTitle) + } else { + Image(systemName: "square") + .font(.largeTitle) + } + } + .foregroundColor(.gray) + .onTapGesture { + if projectId == project.projectId { + projectId = 0 + } else { + projectId = project.projectId // 새로운 프로젝트 선택 + } + } + } + } + } + } + .frame(maxHeight: 200) // List의 높이 제한 + .background(Color.white) // 배경색 + .cornerRadius(8) + .shadow(radius: 5) // 그림자 효과 + .padding(.top) + } + + // 일정 선택 버튼 + Button(action: { + isScheduleListVisible.toggle() // 버튼 클릭 시 리스트 토글 + }) { + Text("일정 선택") + .foregroundColor(.black) + .frame(maxWidth: 100, maxHeight: 40) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + } + + // 참가자 목록이 표시되는 부분 + if isScheduleListVisible { + VStack { + if let availableSchedule = scheduleProjectData { + List(availableSchedule, id: \.self) { schedule in + HStack { + Text(schedule.name) + Spacer() + ZStack { + if scheduleId == schedule.id { // 선택된 프로젝트가 있는지 확인 + Image(systemName: "checkmark.square.fill") + .font(.largeTitle) + } else { + Image(systemName: "square") + .font(.largeTitle) + } + } + .foregroundColor(.gray) + .onTapGesture { + if scheduleId == schedule.id { + scheduleId = 0 + } else { + scheduleId = schedule.id // 새로운 프로젝트 선택 + } + } + } + } + } + } + .frame(maxHeight: 200) // List의 높이 제한 + .background(Color.white) // 배경색 + .cornerRadius(8) + .shadow(radius: 5) // 그림자 효과 + .padding(.top) + .task { + do { + let scheduleProjectResponse = try await get(url: "https://www.bestbirthday.co.kr:8080/api/schedule/project" + "/\(projectId)", responseType: [ScheduleProject]?.self) + // Handle the response here + print(scheduleProjectResponse) + + scheduleProjectData = scheduleProjectResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + } + + // 참가자 선택 버튼 + Button(action: { + isParticipantsListVisible.toggle() // 버튼 클릭 시 리스트 토글 + }) { + Text("수신자 선택") + .foregroundColor(.black) + .frame(maxWidth: 100, maxHeight: 40) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + } + + // 참가자 목록이 표시되는 부분 + if isParticipantsListVisible { + VStack { + if let availableParticipants = projectData?.participants { + List(availableParticipants, id: \.self) { participant in + HStack { + Text(participant) + Spacer() + ZStack { + if receivers.contains(participant) { + Image(systemName: "checkmark.square.fill") + .font(.largeTitle) + } else { + Image(systemName: "square") + .font(.largeTitle) + } + }.foregroundColor(.gray) + .onTapGesture { + if receivers.contains(participant) { + receivers = receivers.filter { + $0 != participant + } + } else { + receivers.append(participant) + } + } + } + } + } + } + .frame(maxHeight: 200) // List의 높이 제한 + .background(Color.white) // 배경색 + .cornerRadius(8) + .shadow(radius: 5) // 그림자 효과 + .padding(.top) + .task { + do { + let projectResponse = try await get(url: "https://www.bestbirthday.co.kr:8080/api/project" + "/\(projectId)", responseType: Project.self) + // Handle the response here + print(projectResponse) + + projectData = projectResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + } + + ZStack(alignment: .topLeading) { + // TextEditor + TextEditor(text: $title) + .padding(10) // TextEditor 내부 여백 + .frame(maxWidth: 350, maxHeight: 50) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + + // Placeholder + if title.isEmpty { + Text("리퀘스트 제목") + .foregroundColor(.customLightGray) + .padding(.top, 20) + .padding(.leading, 11) + } + } + + ZStack(alignment: .topLeading) { + // TextEditor + TextEditor(text: $content) + .padding(10) // TextEditor 내부 여백 + .frame(maxWidth: 350, maxHeight: 200) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + + // Placeholder + if content.isEmpty { + Text("리퀘스트 설명") + .foregroundColor(.customLightGray) + .padding(.top, 20) + .padding(.leading, 11) + } + } + } + .modalPresentation() + .task { + do { + let projectListResponse = try await get(url: "https://www.bestbirthday.co.kr:8080/api/project/list?userId=1", responseType: ProjectList.self) + // 프로젝트 리스트 데이터를 업데이트 + projectListData = projectListResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + } +} + +// 리퀘스트 거부 모달 컴포넌트 +struct modalRequestRejectView: View { + @Environment(\.dismiss) var dismiss + @Binding var comment: String + + // 완료 버튼 활성화 조건 + private var isCompleteButtonEnabled: Bool { + !comment.isEmpty + } + + var body: some View { + VStack(spacing: 20) { + HStack { + Text("리퀘스트 거부") + .titleFont() + .frame(maxWidth: .infinity, alignment: .leading) + + Button(action: { + Task { + do { +// let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/request" + "?projectId=\(projectId)" + "&scheduleId=\(scheduleId)" + "&receivers=string&sender=0&title=string&content=string", body: RequestSchedule(projectId: selectedPid, name: name, startDate: startDate, endDate: endDate, participants: participants, description: description, tag: tag, url: url), responseType: ScheduleInfo.self) +// print(response) + + dismiss() + } catch { + print("리퀘스트 전송 실패: \(error.localizedDescription)") + } + } + }) { + Text("전송") + .frame(maxWidth: .infinity, alignment: .trailing) + .foregroundColor(isCompleteButtonEnabled ? .blue : .gray) // 버튼 색상 변경 + } + .disabled(!isCompleteButtonEnabled) // 활성화 조건에 따라 버튼 활성화/비활성화 + } + .padding(.leading) + .padding(.trailing) + Divider() + + ZStack(alignment: .topLeading) { + // TextEditor + TextEditor(text: $comment) + .padding(10) // TextEditor 내부 여백 + .frame(maxWidth: 350, maxHeight: 200) // 크기 설정 + .background(Color.white) // 배경색 설정 + .cornerRadius(8) // 둥근 모서리 + .shadow(radius: 5) // 약간의 그림자 효과 + .overlay( + RoundedRectangle(cornerRadius: 8) + .stroke(Color.gray, lineWidth: 1) // 테두리 스타일 + ) + + // Placeholder + if comment.isEmpty { + Text("거부 사유") + .foregroundColor(.customLightGray) + .padding(.top, 20) + .padding(.leading, 11) + } + } } .modalPresentation() } @@ -157,7 +1127,6 @@ struct ModalPresentationModifier: ViewModifier { func body(content: Content) -> some View { content - .padding(.horizontal) .padding(.top) .padding(.bottom) .presentationDragIndicator(.visible) diff --git a/noto-App/noto-App/Component/Project_Progress_Component.swift b/noto-App/noto-App/Component/Project_Progress_Component.swift index fa98348..d7b38b4 100644 --- a/noto-App/noto-App/Component/Project_Progress_Component.swift +++ b/noto-App/noto-App/Component/Project_Progress_Component.swift @@ -1,7 +1,7 @@ import SwiftUI struct barGraphComponent: View { - var progress: Double + var progress: Int var width: CGFloat var height: CGFloat @@ -10,22 +10,25 @@ struct barGraphComponent: View { Capsule() .frame(width: width, height: height) .foregroundColor(.customLightGray) - ZStack(alignment: .center) { - Capsule() - .frame(width: width * progress, height: height) - .foregroundColor(.customBlue) - .animation(.easeInOut, value: progress) - Text("\(progress * 100, specifier: "%.2f")%") - .font(.custom("Freesentation-7Bold", size: 9)) - .foregroundColor(.customLightGray) - } + ZStack(alignment: .center) { + // Foreground capsule with blue color + Capsule() + .frame(width: width * CGFloat(progress) / 100, height: height) // Convert progress to CGFloat and scale by 100% + .foregroundColor(.customBlue) + .animation(.easeInOut, value: progress) + + // Text showing the progress as a percentage + Text("\(progress, specifier: "%.d")%") + .font(.custom("Freesentation-7Bold", size: 9)) + .foregroundColor(.customLightGray) + } } } } struct simpleProgressRow: View { var title: String - var progress: Double + var progress: Int var Dday : Int var action: () -> Void @@ -43,7 +46,7 @@ struct simpleProgressRow: View { .foregroundColor(.customBlack) Spacer() - Text("D-\(Dday)") + Text("D\(Dday)") .descriptionFont() .padding(.trailing, 5) } diff --git a/noto-App/noto-App/Component/Request_Component.swift b/noto-App/noto-App/Component/Request_Component.swift index 8f7d907..d71e738 100644 --- a/noto-App/noto-App/Component/Request_Component.swift +++ b/noto-App/noto-App/Component/Request_Component.swift @@ -2,7 +2,7 @@ import SwiftUI // 리퀘스트 및 알림 컴포넌트 struct requestComponent: View { - @State var req_count: Int + let req_count: Int var action: () -> Void var body: some View { diff --git a/noto-App/noto-App/DataModel/Project_Model.swift b/noto-App/noto-App/DataModel/Project_Model.swift new file mode 100644 index 0000000..e24e814 --- /dev/null +++ b/noto-App/noto-App/DataModel/Project_Model.swift @@ -0,0 +1,50 @@ +// +// Project_Model.swift +// noto-App +// +// Created by 박상현 on 12/6/24. +// +import SwiftUI + +struct ProjectProgressDetail: Codable, Hashable { + let id: Int + let name: String + let progress: Int? + let participants: [String] + let startDate: Date + let endDate: Date + let dday: Int +} + +struct ProjectProgress: Codable { + let projectProgress: [ProjectProgressDetail] +} + +struct Project: Codable { + let id: Int + let name: String + let startDate: Date + let endDate: Date + let participants: [String]? + let description: String + let url: String + let imageurl: String +} + +struct ProjectsDetail: Codable, Hashable { + let name: String + let projectId: Int + let description: String +} + +struct ProjectList: Codable { + let projects: [ProjectsDetail] +} + +struct RequestProject: Codable { + let name: String + let startDate: Date + let endDate: Date + let description: String + let image: String +} diff --git a/noto-App/noto-App/DataModel/Request_Model.swift b/noto-App/noto-App/DataModel/Request_Model.swift new file mode 100644 index 0000000..c970002 --- /dev/null +++ b/noto-App/noto-App/DataModel/Request_Model.swift @@ -0,0 +1,56 @@ +// +// Request_Model.swift +// noto-App +// +// Created by 박상현 on 12/6/24. +// +import SwiftUI + +struct RequestNumber: Codable { + let numberOfRequest: Int +} + +struct RequestListDetail: Codable, Hashable { + let requestId: Int + let title: String + let sender: String +} + +struct RequestList: Codable { + let sendRequest: [RequestListDetail] + let receiveRequest: [RequestListDetail] +} + +struct RequestInfo: Codable { + let id: Int + let project: String + let schedule: String? + let startDate: Date + let endDate: Date + let receivers: [String]? + let sender: String + let title: String + let content: String + let isSender: Bool +} + +struct RequestReceived: Codable, Hashable { + let receiverName: String + let comment: String + let status: String +} + +struct RequestRequest: Codable, Hashable { + let projectId: Int + let scheduleId: Int + let receivers: [String] + let sender: Int + let title: String + let content: String +} + +struct RequestRequestReceived: Codable, Hashable { + let receiverId: Int + let comment: String + let status: String +} diff --git a/noto-App/noto-App/DataModel/Todo_Model.swift b/noto-App/noto-App/DataModel/Todo_Model.swift new file mode 100644 index 0000000..0070871 --- /dev/null +++ b/noto-App/noto-App/DataModel/Todo_Model.swift @@ -0,0 +1,48 @@ +// +// Todo_Model.swift +// noto-App +// +// Created by 박상현 on 12/6/24. +// +import SwiftUI + +struct ScheduleTodayDetail: Codable, Hashable { + let id: Int + let name: String + let description: String + let endDate: Date +} + +struct ScheduleToday: Codable { + let todaySchedule: [ScheduleTodayDetail] +} + +struct ScheduleInfo: Codable { + let id: Int + let name: String + let startDate: Date + let endDate: Date + let participants: [String] + let description: String + let tag: String + let url: String +} + +struct ScheduleProject: Codable, Hashable { + let id: Int + let name: String + let description: String + let startDate: Date + let endDate: Date +} + +struct RequestSchedule: Codable { + let projectId: Int + let name: String + let startDate: Date + let endDate: Date + let participants: [String] + let description: String + let tag: String + let url: String +} diff --git a/noto-App/noto-App/DataModel/User_Model.swift b/noto-App/noto-App/DataModel/User_Model.swift new file mode 100644 index 0000000..7d4ac98 --- /dev/null +++ b/noto-App/noto-App/DataModel/User_Model.swift @@ -0,0 +1,12 @@ +// +// User_Model.swift +// noto-App +// +// Created by 박상현 on 12/10/24. +// +import SwiftUI + +struct RequestUserParticipate: Codable { + let userId: Int + let projectId: Int +} diff --git a/noto-App/noto-App/Page/Main_Page.swift b/noto-App/noto-App/Page/Main_Page.swift index 48b5312..165c315 100644 --- a/noto-App/noto-App/Page/Main_Page.swift +++ b/noto-App/noto-App/Page/Main_Page.swift @@ -1,25 +1,134 @@ import SwiftUI enum Tab { case settings, home, projects } -enum Page { case main, requestList, requestDetail, todoDetail, progressList, todoList, projectDetail } +enum Page { case main, requestList, requestDetail, todoDetail, progressList, projectDetail, + settings, user, pw, alarm, display, client, faq, info } +enum Modal { case todoList, sendRequestList, receiveRequestList, project } struct MainPage_ContentView: View { - @State private var selectedTab: Tab = .home + @State private var projectListData: ProjectList? = nil + @State var currentScreen: Page = .main + @State var prevScreen: Page = .main + @State var selectedPid: Int = 0 + @State var selectedSid: Int = 0 + + @State private var prevTab: Tab = .home + @State var selectedTab: Tab = .home + @State private var showingSheet: Bool = false + + let url = "https://www.bestbirthday.co.kr:8080/api" - var body: some View { - TabView(selection: $selectedTab) { - SettingPage() - .tabItem { Image(systemName: "gear") } - .tag(Tab.settings) - mainPage(selectedTab: $selectedTab) - .tabItem { Image(systemName: "house") } - .tag(Tab.home) - ProjectSelectionView() - .tabItem { Image(systemName: "folder") } - .tag(Tab.projects) + var body: some View { +// ZStack{ +// // TabView는 여전히 화면 전환을 처리합니다 +// TabView(selection: $selectedTab) { +// SettingPage() +// .tabItem { Image(systemName: "gear") } +// .tag(Tab.settings) +// +// mainPage(selectedTab: $selectedTab) +// .tabItem { Image(systemName: "house") } +// .tag(Tab.home) +// ProjectSelectionView() +// .tabItem { Image(systemName: "folder") } +// .tag(Tab.projects) +// } +// // Custom Button을 TabBar 위에 덮어 놓기 +// VStack { +// HStack{ +// Button(action: { +// print("Custom button tapped.") +// showingSheet = true +// }) { +// ZStack{ +// // 큰 배경 박스를 만들기 위한 Rectangle +// Rectangle() +// .fill(Color.customBackgroundColor) +// .frame(width: 135, height: 75) // 버튼 크기 조정 +// .offset(x: 0, y: 20) +// Image(systemName: "folder") +// .resizable() +// .frame(width: 24, height: 21) // 버튼 크기 조정 +// .foregroundColor(.gray) +// } +// } +// .frame(width: 30, height: 30) +// } +// .frame(maxHeight: 750, alignment: .bottom) +// } +// .frame(maxWidth: 300, alignment: .trailing) +// } + + TabView(selection: $selectedTab) { + SettingPage(currentScreen: $currentScreen) + .tabItem { Image(systemName: "gear") } + .tag(Tab.settings) + mainPage(currentScreen: $currentScreen, prevScreen: $prevScreen, selectedSid: $selectedSid, selectedPid: $selectedPid) + .tabItem { Image(systemName: "house") } + .tag(Tab.home) + if currentScreen != .projectDetail { + if prevTab == .home { + mainPage(currentScreen: $currentScreen, prevScreen: $prevScreen, selectedSid: $selectedSid, selectedPid: $selectedPid) + .tabItem { Image(systemName: "folder") } + .tag(Tab.projects) + } + else if prevTab == .settings { + SettingPage(currentScreen: $currentScreen) + .tabItem { Image(systemName: "folder") } + .tag(Tab.projects) + } + } else { + ProjectPage(currentScreen: $currentScreen, selectedPid: $selectedPid, selectedSid: $selectedSid) + .tabItem { Image(systemName: "folder") } + .tag(Tab.projects) + } + } + .background(Color.white) + .onChange(of: selectedTab) { newTab in + if newTab == .projects { + if currentScreen != .projectDetail { + showingSheet = true + } + } else { + prevTab = newTab + if newTab == .home { + currentScreen = .main + } else { + currentScreen = .settings + } + } + } + .onChange(of: currentScreen) { newScreen in + if newScreen == .projectDetail { + selectedTab = .projects + } + } + .sheet(isPresented: $showingSheet, onDismiss: { + showingSheet = false + if currentScreen == .main || currentScreen == .settings { + selectedTab = prevTab + } + }){ + VStack { + // 데이터가 로딩된 후 실제 모달 화면 표시 + if let projectListData = projectListData { + modalProjectListView(projectListData: $projectListData, currentScreen: $currentScreen, selectedPid: $selectedPid) + } + } + .task { + // 비동기 작업을 통해 데이터를 가져옴 + do { + let projectListResponse = try await get(url: url + "/project/list?userId=1", responseType: ProjectList.self) + // 프로젝트 리스트 데이터를 업데이트 + DispatchQueue.main.async { + projectListData = projectListResponse.data + } + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + } } - .background(Color.white) - } } struct MainPage_ContentView_Preview: PreviewProvider { @@ -30,13 +139,23 @@ struct MainPage_ContentView_Preview: PreviewProvider { struct mainPage: View { @State private var searchText = "" - @State private var currentScreen: Page = .main - @State private var selectedId: Int = 0 - @State private var selectedRid: Int = 0 - @State private var selectedRequestType: Int = 0 + @Binding var currentScreen: Page + @Binding var prevScreen: Page + @State var selectedId: Int = 0 + @Binding var selectedSid: Int + @Binding var selectedPid: Int + @State var selectedRid: Int = 0 + @State private var selectedRequestType: String = "" @State private var selectedRequestRejected: Int = 0 - @Binding var selectedTab: Tab - + @State private var showingSheet: Bool = false + + @State private var scheduleTodayData: ScheduleToday? = nil + + @State private var requestNumberData: RequestNumber? = nil + + @State private var projectProgressData: ProjectProgress? = nil + + let url = "https://www.bestbirthday.co.kr:8080/api" var body: some View { VStack{ @@ -45,40 +164,52 @@ struct mainPage: View { VStack(spacing: 20) { mainHeader() searchBar(searchText: $searchText, action: {print("검색창 클릭 수정 필요")}) - requestComponent(req_count: 5, action: { currentScreen = .requestList }) + if let requestNumberData = requestNumberData { + requestComponent(req_count: requestNumberData.numberOfRequest, action: { currentScreen = .requestList }) + } VStack(spacing: 10) { Spacer() - ForEach(todolist.indices, id: \.self) { index in - HStack { - let todo = todolist[index] - rowComponent( - imageName: todo.imageName, - title: todo.title, - subtitle: todo.subtitle, - action: { - currentScreen = .todoDetail - selectedId = index // 클릭된 인덱스 저장 + if let scheduleTodayData = scheduleTodayData { + // 처음 5개 항목만 처리 + ForEach(scheduleTodayData.todaySchedule.prefix(5), id: \.self) { todaySchedule in + HStack { + rowComponent( + imageName: "user", + title: todaySchedule.name, + subtitle: todaySchedule.description, + action: { + currentScreen = .todoDetail + selectedSid = todaySchedule.id + } + ) + } + + // 마지막 항목에 대해 다른 처리를 할 수 있습니다. + if todaySchedule != scheduleTodayData.todaySchedule.prefix(5).last { + Divider().padding(.horizontal, 20) + } } - ) } - if index < todolist.count - 1 { - Divider() - .padding(.horizontal, 20) - } - } - viewAllComponent(title: "오늘 내 할 일 모두 보기", action: {currentScreen = .todoList}) + + viewAllComponent(title: "오늘 내 할 일 모두 보기", action: { + showingSheet = true + }) } .blockStyle(height: .infinity) VStack(spacing: 10) { - ForEach(projectList.indices, id: \.self) { index in - simpleProgressRow(title: projectList[index].name, - progress: projectList[index].progress, - Dday: calculateDDay(from: projectList[index].startDate, to: projectList[index].endDate), - action: { currentScreen = .projectDetail - selectedId = projectList[index].pid}) - } + if let projectProgressData = projectProgressData { + // 처음 1개 항목만 처리 + ForEach(projectProgressData.projectProgress.prefix(1), id: \.self) { projectProgress in + if let progress = projectProgress.progress{ + simpleProgressRow(title: projectProgress.name, + progress: progress, + Dday: -projectProgress.dday, + action: { currentScreen = .projectDetail }) + } + } + } viewAllComponent(title: "프로젝트 진행 현황 모두 보기", action: {currentScreen = .progressList}) } @@ -90,27 +221,50 @@ struct mainPage: View { .backgroundStyle() } .scrollViewStyle() + .task { + do { + let scheduleTodayResponse = try await get(url: url + "/schedule/today?userId=1", responseType: ScheduleToday.self) + // Handle the response here +// print(scheduleTodayResponse) + + scheduleTodayData = scheduleTodayResponse.data + + let requestNumberResponse = try await get(url: url + "/request/number?userId=1", responseType: RequestNumber.self) + // Handle the response here +// print(requestNumberResponse) + + requestNumberData = requestNumberResponse.data + + let projectProgressResponse = try await get(url: url + "/project/progress?userId=1", responseType: ProjectProgress.self) + // Handle the response here +// print(projectProgressResponse) + + projectProgressData = projectProgressResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + .sheet(isPresented: $showingSheet, onDismiss: { + showingSheet = false + }){ + modalTodayTodoListView(scheduleTodayData: $scheduleTodayData, currentScreen: $currentScreen, selectedSid: $selectedSid) + } + .onAppear() { + prevScreen = .main + } } else if(currentScreen == .requestList) { - RequestListPage(currentScreen: $currentScreen, - selectedRid: $selectedRid, - selectedRequestType: $selectedRequestType, - selectedRequestRejected: $selectedRequestRejected) + RequestListPage(currentScreen: $currentScreen, selectedRid: $selectedRid, prevScreen: prevScreen) } else if(currentScreen == .todoDetail) { - TodoDetialPage(currentScreen: $currentScreen, prevScreen: .main, index: selectedId) + TodoDetialPage(currentScreen: $currentScreen, selectedSid: $selectedSid, selectedPid: $selectedPid, prevScreen: prevScreen) } else if(currentScreen == .progressList) { - ProgressDetailPage(currentScreen: $currentScreen, prevScreen: .main, pid: selectedId) - } else if(currentScreen == .todoList) { - // 여기는 모달 올라오게 만들기 + ProgressDetailPage(currentScreen: $currentScreen, selectedPid: $selectedPid, prevScreen: .main) } else if(currentScreen == .requestDetail) { - RequestDetailPage(currentScreen: $currentScreen, - prevScreen: .main, - rid: selectedRid, - type: selectedRequestType, - rejected: selectedRequestRejected) + RequestDetailPage(currentScreen: $currentScreen, selectedRid: $selectedRid, prevScreen: .requestList) } else if(currentScreen == .projectDetail) { - ProjectPage(currentScreen: $currentScreen, - prevScreen: currentScreen, - pid: selectedId) + ProjectPage(currentScreen: $currentScreen, selectedPid: $selectedPid, selectedSid: $selectedSid) + .onAppear() { + prevScreen = .projectDetail + } } } } diff --git a/noto-App/noto-App/Page/ProgressList_Page.swift b/noto-App/noto-App/Page/ProgressList_Page.swift index 7681a1f..bf96c85 100644 --- a/noto-App/noto-App/Page/ProgressList_Page.swift +++ b/noto-App/noto-App/Page/ProgressList_Page.swift @@ -1,25 +1,27 @@ import SwiftUI -struct ProgressDetailPage_ContentView: View { - @State private var currentScreen: Page = .progressList - @State private var prevScreen: Page = .main - @State private var clickedIndex: Int = 1 - var body: some View { - ProgressDetailPage(currentScreen: $currentScreen, prevScreen: prevScreen, pid: clickedIndex) - } -} - -struct ProgressDetailPage_ContentView_Preview: PreviewProvider { - static var previews: some View { - ProgressDetailPage_ContentView() - } -} +//struct ProgressDetailPage_ContentView: View { +// @State private var currentScreen: Page = .progressList +// @State private var prevScreen: Page = .main +// @State private var selectedPid: Int = 0 +// var body: some View { +// ProgressDetailPage(currentScreen: $currentScreen, selectedPid: $selectedPid, prevScreen: .main) +// } +//} +// +//struct ProgressDetailPage_ContentView_Preview: PreviewProvider { +// static var previews: some View { +// ProgressDetailPage_ContentView() +// } +//} struct ProgressDetailPage: View { @State private var searchText = "" + @State private var projectProgressData: ProjectProgress? = nil @Binding var currentScreen: Page + @Binding var selectedPid: Int var prevScreen: Page - var pid: Int + let url = "https://www.bestbirthday.co.kr:8080/api" var body: some View { VStack { @@ -29,24 +31,42 @@ struct ProgressDetailPage: View { goBackHeader(goBackAction: { currentScreen = prevScreen }) searchBar(searchText: $searchText, action: { print("검색 버튼 수정 필요") }) - ForEach(projectList.indices, id: \.self) { index in - VStack { - VStack(alignment: .leading, spacing: 20) { - Spacer() - projectTitleRow(imageName: projectList[index].imageName, name: projectList[index].name) - barGraphComponent(progress: projectList[index].progress, width: 335, height: 20) - projectContentRows(projectIndex: index) - .padding(.bottom, 10) - } - viewAllComponent(title: "프로젝트 페이지로 이동", action: {currentScreen = .projectDetail}) + if let projectProgressData = projectProgressData { + // 처음 1개 항목만 처리 + ForEach(projectProgressData.projectProgress, id: \.self) { projectProgress in + VStack { + VStack(alignment: .leading, spacing: 20) { + Spacer() + projectTitleRow(imageName: "projectImage", name: projectProgress.name) + if let progress = projectProgress.progress { + barGraphComponent(progress: progress, width: 335, height: 20) + } + projectContentRows(projectProgress: projectProgress) + } + viewAllComponent(title: "프로젝트 페이지로 이동", action: { + currentScreen = .projectDetail + selectedPid = projectProgress.id + }) + } + .blockStyle(height: .infinity) + } } - .blockStyle(height: .infinity) - } } .backgroundStyle() } .scrollViewStyle() + .task { + do { + let projectProgressResponse = try await get(url: url + "/project/progress?userId=1", responseType: ProjectProgress.self) + // Handle the response here + print(projectProgressResponse) + + projectProgressData = projectProgressResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } } } } @@ -66,42 +86,51 @@ struct projectTitleRow: View { } struct projectContentRows: View { - var projectIndex: Int - + let projectProgress: ProjectProgressDetail + + private var dateFormatter: DateFormatter { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + return formatter + } + var body: some View { VStack(spacing: 10) { // 프로젝트 참여자 정보 출력 HStack { Text("프로젝트 참여자") .projectContentFont() - .padding(.trailing, 10) - HStack { - ForEach(projectList[projectIndex].participants.indices, id: \.self) { index in - Text("\(projectList[projectIndex].participants[index].name)") - .projectContentFont() + ScrollView(.horizontal, showsIndicators: false) { // Horizontal ScrollView 추가 + HStack { + ForEach(projectProgress.participants, id: \.self) { participant in + Text("\(participant)") + .projectContentFont() + .lineLimit(1) // 한 줄로 제한 + } + } } - } } + .frame(maxWidth: 333, alignment: .leading) // 프로젝트 시작일 정보 출력 HStack { Text("프로젝트 시작일") .projectContentFont() - .padding(.trailing, 10) - Text("\(dateFormatter.string(from: projectList[projectIndex].startDate))") + Text("\(dateFormatter.string(from: projectProgress.startDate))") .projectContentFont() .submitLabel(.continue) } + .frame(maxWidth: 333, alignment: .leading) // 프로젝트 마감일 정보 출력 HStack { Text("프로젝트 마감일") .projectContentFont() - .padding(.trailing, 10) - Text("\(dateFormatter.string(from: projectList[projectIndex].endDate))") + Text("\(dateFormatter.string(from: projectProgress.endDate))") .projectContentFont() } + .frame(maxWidth: 333, alignment: .leading) } } } diff --git a/noto-App/noto-App/Page/Project_Page.swift b/noto-App/noto-App/Page/Project_Page.swift index d92dd2b..68e5bda 100644 --- a/noto-App/noto-App/Page/Project_Page.swift +++ b/noto-App/noto-App/Page/Project_Page.swift @@ -1,131 +1,203 @@ import SwiftUI -struct ProjectPage_ContentView: View { - @State private var currentScreen: Page = .projectDetail - @State private var prevScreen: Page = .main - @State private var pid: Int = 0 - var body: some View { - ProjectPage(currentScreen: $currentScreen, - prevScreen: prevScreen, - pid: pid) - } -} - -struct ProjectPage_ContentView_Preview: PreviewProvider { - static var previews: some View { - ProjectPage_ContentView() - } -} +//struct ProjectPage_ContentView: View { +// @State private var currentScreen: Page = .projectDetail +// @State var selectedId: Int = 0 +// @State private var prevScreen: Page = .main +// @State private var pid: Int = 0 +// var body: some View { +// ProjectPage(currentScreen: $currentScreen, selectedId: $selectedId, +// prevScreen: prevScreen, +// pid: pid) +// } +//} +// +//struct ProjectPage_ContentView_Preview: PreviewProvider { +// static var previews: some View { +// ProjectPage_ContentView() +// } +//} struct ProjectPage: View { - @State private var searchText = "" - @State var selectedDate: Date = Date() - @Binding var currentScreen: Page - @Namespace var todoSection - var prevScreen: Page - var pid: Int - let calendar = Calendar.current - - var body: some View { - let project = projectList.filter { project in - return project.pid == pid // startDate와 endDate 사이인지 확인 - } + @State private var searchText = "" + @State var selectedDate: Date = Date() + @State private var requestNumberData: RequestNumber? = nil + @State private var projectData: Project? = nil + @State private var scheduleProjectData: [ScheduleProject]? = nil + @State private var showingModalProject: Bool = false + @State private var showingModalTodoList: Bool = false + @Binding var currentScreen: Page + @Binding var selectedPid: Int + @Binding var selectedSid: Int + @Namespace var todoSection + let calendar = Calendar.current + let url = "https://www.bestbirthday.co.kr:8080/api" - VStack { - ScrollViewReader { proxy in - ScrollView { - VStack(spacing: 20) { - mainHeader() - requestComponent(req_count: 5, action: { currentScreen = .requestList }) - - VStack{ - VStack(alignment: .leading, spacing: 5) { - Spacer() - titleRow_3(title: project[0].name, optionAction: {print("프로젝트 설정 버튼 클릭 (수정 필요)")}) - .padding(.top, 10) - Divider() - .padding(10) - VStack(alignment: .leading, spacing: 15){ - dateRow(startDate: project[0].startDate, endDate: project[0].endDate) - participantRow() - Divider() - .padding(.horizontal, 10) - descriptionRow(description: project[0].description) + var body: some View { + VStack { + ScrollViewReader { proxy in + ScrollView { + VStack(spacing: 20) { + mainHeader() + + if let requestNumberData = requestNumberData { + requestComponent(req_count: requestNumberData.numberOfRequest, action: { + currentScreen = .requestList + }) + } + + if let projectData = projectData { + VStack { + VStack(alignment: .leading, spacing: 5) { + Spacer() + titleRow_3(title: projectData.name, optionAction: { + print("프로젝트 설정 버튼 클릭 (수정 필요)") + showingModalProject = true + }) + .padding(.top, 10) + .sheet(isPresented: $showingModalProject, onDismiss: { + showingModalProject = false + }) { + modalProjectView(modifiedPid: $selectedPid) + } + Divider() + .padding(10) + VStack(alignment: .leading, spacing: 15){ + dateRow(startDate: projectData.startDate, endDate: projectData.endDate) + if let participants = projectData.participants { + participantRow(participants: participants) + } + Divider() + .padding(.horizontal, 10) + descriptionRow(description: projectData.description) + } + Spacer() + } + } + .blockStyle(height: .infinity) + + VStack { + Spacer() + CalendarView(currentScreen: $currentScreen, selectedPid: $selectedPid, selectedSid: $selectedSid, selectedDate: $selectedDate, + onDateSelected: { + withAnimation { proxy.scrollTo(todoSection, anchor: .bottom) } + }, project: projectData) + .padding(.top, 10) + } + .blockStyle(height: .infinity) + } + + VStack(spacing: 10) { + Spacer() + if let scheduleProjectData = scheduleProjectData { + let filteredScheduleData = scheduleProjectData.filter { schedule in + return (schedule.startDate...schedule.endDate).contains(selectedDate) + } + + if filteredScheduleData.isEmpty { + Text("선택된 날짜에 할 일이 없습니다.") + .foregroundColor(.gray) + .padding() + } else { + ForEach(filteredScheduleData.prefix(5), id: \.id) { schedule in + HStack { + rowComponent( + imageName: "user", + title: schedule.name, + subtitle: schedule.description, + action: { + currentScreen = .todoDetail + selectedSid = schedule.id + } + ) + } + if schedule != filteredScheduleData.prefix(5).last { + Divider().padding(.horizontal, 20) + } + } + + viewAllComponent(title: "할 일 모두 보기", action: { + showingModalTodoList = true + }) + .sheet(isPresented: $showingModalTodoList, onDismiss: { + showingModalTodoList = false + }) { + modalProjectTodoListView(scheduleProjectData: filteredScheduleData, currentScreen: $currentScreen, selectedSid: $selectedSid) + } + } + } + } + .blockStyle(height: .infinity) + + dumyBottom() + .id(todoSection) + } + .backgroundStyle() } - Spacer() - } - } - .blockStyle(height: .infinity) - - VStack{ - Spacer() - CalendarView(selectedDate: $selectedDate, - onDateSelected: { - withAnimation{ proxy.scrollTo(todoSection, anchor: .bottom) } - }, project: project[0]) - .padding(.top, 10) - } - .blockStyle(height: .infinity) - - VStack(spacing: 10) { - Spacer() - - let filteredTodos = todolist.filter { todo in - return (todo.startDate...todo.endDate).contains(selectedDate) // startDate와 endDate 사이인지 확인 - } - - if filteredTodos.isEmpty { - Text("선택된 날짜에 할 일이 없습니다.") - .foregroundColor(.gray) - .padding() - } else { - ForEach(filteredTodos.indices, id: \.self) { index in - let todo = filteredTodos[index] - HStack { - rowComponent( - imageName: todo.imageName, - title: todo.title, - subtitle: todo.subtitle, - action: { - currentScreen = .todoDetail - //clickedIndex = index // 클릭된 인덱스 저장 - } - ) - } - if index < filteredTodos.count - 1 { - Divider() - .padding(.horizontal, 20) - } + .scrollViewStyle() + .task { + do { + let projectResponse = try await get(url: url + "/project" + "/\(selectedPid)", responseType: Project.self) + projectData = projectResponse.data + + let requestNumberResponse = try await get(url: url + "/request/number?userId=1", responseType: RequestNumber.self) + requestNumberData = requestNumberResponse.data + + let scheduleProjectResponse = try await get(url: url + "/schedule/project" + "/\(selectedPid)", responseType: [ScheduleProject]?.self) + scheduleProjectData = scheduleProjectResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } } - } - viewAllComponent(title: "오늘 내 할 일 모두 보기", action: {currentScreen = .todoList}) } - .blockStyle(height: .infinity) - - dumyBottom() - .id(todoSection) - } - .backgroundStyle() } - .scrollViewStyle() - } } - } } + struct CalendarView: View { // 현재 월 및 연도 @State private var currentDate = Date() + @State private var showingSheet = false + @State private var scheduleProjectData: [ScheduleProject]? = nil + @Binding var currentScreen: Page + @Binding var selectedPid: Int + @Binding var selectedSid: Int @Binding var selectedDate: Date var onDateSelected: () -> Void let calendar = Calendar.current - let project: project + let project: Project var body: some View { VStack(alignment: .leading) { Spacer() - titleRow_3(title: "\(monthAndYear(for: currentDate)) 캘린더", optionAction: {print("캘린더 설정 버튼 클릭 (수정 필요)")}) + titleRow_3(title: "\(monthAndYear(for: currentDate)) 캘린더", optionAction: { + print("캘린더 설정 버튼 클릭 (수정 필요)") + showingSheet = true + }) + .sheet(isPresented: $showingSheet, onDismiss: { + showingSheet = false + }){ + VStack { + // 데이터가 로딩된 후 실제 모달 화면 표시 + if let scheduleProjectData = scheduleProjectData { + modalTodoListView(scheduleProjectData: $scheduleProjectData, currentScreen: $currentScreen, selectedPid: $selectedPid, selectedSid: $selectedSid) + } + } + .task { + // 비동기 작업을 통해 데이터를 가져옴 + do { + let scheduleProjectResponse = try await get(url: "https://www.bestbirthday.co.kr:8080/api/schedule/project" + "/\(selectedPid)", responseType: [ScheduleProject]?.self) + // 프로젝트 리스트 데이터를 업데이트 + DispatchQueue.main.async { + scheduleProjectData = scheduleProjectResponse.data + } + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + } Divider() .padding(.horizontal, 20) diff --git a/noto-App/noto-App/Page/RequestDetail_Page.swift b/noto-App/noto-App/Page/RequestDetail_Page.swift index d654d34..dddae71 100644 --- a/noto-App/noto-App/Page/RequestDetail_Page.swift +++ b/noto-App/noto-App/Page/RequestDetail_Page.swift @@ -1,29 +1,42 @@ import SwiftUI -struct RequestDetailPage_ContentView: View { - @State private var currentScreen: Page = .todoDetail - @State private var prevScreen: Page = .main - @State private var rid: Int = 1 - @State private var type: Int = 1 - @State private var rejected: Int = 1 - var body: some View { - RequestDetailPage(currentScreen: $currentScreen, prevScreen: prevScreen, rid: rid, type: type, rejected: rejected) - } -} - -struct RequestDetailPage_ContentView_Preview: PreviewProvider { - static var previews: some View { - RequestDetailPage_ContentView() - } -} +//struct RequestDetailPage_ContentView: View { +// @State private var currentScreen: Page = .todoDetail +// @State private var prevScreen: Page = prev +// @State private var rid: Int = 1 +// @State private var type: Int = 1 +// @State private var rejected: Int = 1 +// var body: some View { +// RequestDetailPage(currentScreen: $currentScreen, prevScreen: prevScreen, rid: rid, type: type, rejected: rejected) +// } +//} +// +//struct RequestDetailPage_ContentView_Preview: PreviewProvider { +// static var previews: some View { +// RequestDetailPage_ContentView() +// } +//} struct RequestDetailPage: View { @Binding var currentScreen: Page + @Binding var selectedRid: Int var prevScreen: Page - var rid: Int // 리퀘스트 아이디 - var type: Int // 리퀘스트 종류 (1: sentRequest, 2: receivedRequest) - var rejected: Int + + @State private var type: Int = 1 + @State private var rejected: Int = 1 + @State private var rid: Int = 1 + + @State private var requestInfoData: RequestInfo? = nil + @State private var requestReceivedData: [RequestReceived]? = nil + @State private var showingSheet: Bool = false + @State private var showingDialog: Bool = false + + @State private var receiverId: Int = 0 + @State private var comment: String = "" + @State private var status: String = "" + + let url = "https://www.bestbirthday.co.kr:8080/api" @State private var person: String = "" @State private var role: String = "" @@ -36,55 +49,111 @@ struct RequestDetailPage: View { var body: some View { VStack { ScrollView { - VStack(spacing: 15) { - dumyHeader() - goBackHeader(goBackAction: {currentScreen = prevScreen}) - - // 리퀘스트 설명 - VStack(spacing: 10){ - Spacer() - titleRow_3(title: title, optionAction: { print("리퀘스트 설정 버튼 클릭 (수정 필요)")}) - .padding(.top, 10) - Divider() - .padding(.horizontal, 20) - .padding(.bottom, 10) - - VStack(spacing: 15) { - if type == 1 { personRow(sender: userInfo.name, receiver: person, type: type) } - else if type == 2 { personRow(sender: person, receiver: userInfo.name, type: type) } - - requestDescriptionRow(description: subtitle) - } - Spacer() + if let requestInfoData = requestInfoData { + VStack(spacing: 15) { + dumyHeader() + goBackHeader(goBackAction: {currentScreen = prevScreen}) + + // 리퀘스트 설명 + VStack(spacing: 10){ + Spacer() + if !requestInfoData.isSender { + titleRow_3(title: title, optionAction: { + print("리퀘스트 설정 버튼 클릭 (수정 필요)") + showingDialog = true + }) + .confirmationDialog("Choose an option", isPresented: $showingDialog) { + Button("수락", role: .none) { + print("수락") + status = "수락" + Task { + let response = try await patch(url: url + "/request" + "/\(selectedRid)", body: RequestRequestReceived(receiverId: receiverId, comment: comment, status: status)) + // Handle the response here + print(response) + } + } + Button("거부", role: .destructive) { + print("거부") + status = "거부" + showingSheet = true + } + Button("취소", role: .cancel) { + print("취소") + } + } + .padding(.top, 10) + .sheet(isPresented: $showingSheet) { + modalRequestRejectView(comment: $comment) + } + } else { + titleRow_1(title: title) + } + Divider() + .padding(.horizontal, 20) + .padding(.bottom, 10) + + VStack(spacing: 15) { + if let receivers = requestInfoData.receivers { + personRow(sender: requestInfoData.sender, receivers: receivers) + } + + requestDescriptionRow(description: requestInfoData.content) + } + Spacer() + } + .blockStyle(height: .infinity) + + if requestInfoData.isSender { + // 리퀘스트 반려 사유 (Comment) + VStack(alignment: .leading, spacing: 10) { + Spacer() + titleRow_1(title: "리퀘스트 반려 사유") + .padding(.top, 10) + Divider() + .padding(.horizontal, 20) + .padding(.bottom, 10) + + if let requestReceivedData = requestReceivedData { + ForEach(requestReceivedData, id: \.self) { requestReceived in + if requestReceived.status == "거절" { + Text ("\(requestReceived.receiverName)") + .subTitleFont() + .padding(.leading, 20) + Text ("\(requestReceived.comment)") + .subTitleFont() + .lineLimit(nil) // 줄바꿈을 허용하고, 필요시 여러 줄로 출력 + .multilineTextAlignment(.leading) // 왼쪽 정렬 + .padding(.leading, 20) + } + } + } + + Spacer() + } + .blockStyle(height: .infinity) + } + } + .backgroundStyle() } - .blockStyle(height: .infinity) - - if rejected == 1 { - // 리퀘스트 반려 사유 (Comment) - VStack(alignment: .leading, spacing: 10) { - Spacer() - titleRow_1(title: "리퀘스트 반려 사유") - .padding(.top, 10) - Divider() - .padding(.horizontal, 20) - .padding(.bottom, 10) + } + .scrollViewStyle() + .task { + do { + let requestInfoResponse = try await get(url: url + "/request/info" + "/\(selectedRid)" + "?userId=1", responseType: RequestInfo.self) + // Handle the response here + print(requestInfoResponse) - VStack(alignment: .leading) { - Text("\(rejectedReason)") - .descriptionFont() - .multilineTextAlignment(.leading) - .lineLimit(10) - } - .padding(.horizontal, 20) + requestInfoData = requestInfoResponse.data + + let requestReceivedResponse = try await get(url: url + "/request_receive" + "/\(selectedRid)", responseType: [RequestReceived].self) + // Handle the response here + print(requestReceivedResponse) - Spacer() - } - .blockStyle(height: .infinity) + requestReceivedData = requestReceivedResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") } - } - .backgroundStyle() } - .scrollViewStyle() } .onAppear { filterRequests() @@ -116,8 +185,7 @@ struct RequestDetailPage: View { struct personRow: View { var sender: String - var receiver: String - var type: Int + var receivers: [String] var body: some View { HStack { @@ -135,18 +203,51 @@ struct personRow: View { .subTitleFont() .submitLabel(.continue) - Image(systemName: type == 1 ? "chevron.right" : "chevron.left") + Image(systemName: "chevron.right") .font(.system(size: 13)) .foregroundColor(.customDarkGray) - Text("\(receiver)") - .subTitleFont() - .submitLabel(.continue) + // ScrollView를 추가하여 가로로 스크롤 할 수 있도록 합니다. + if totalTextWidth(receivers) > 100 { + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 8) { + // 텍스트들 사이에 간격을 추가합니다. + ForEach(receivers, id: \.self) { receiver in + Text(receiver) + .subTitleFont() + .lineLimit(1) + } + } + .frame(maxWidth: .infinity, alignment: .trailing) // 오른쪽 정렬 + } + .frame(maxWidth: 100) // 최대 너비 200 + } else { + // 텍스트가 200 이하일 경우, 오른쪽 정렬 + HStack(spacing: 8) { + Spacer() + ForEach(receivers, id: \.self) { receiver in + Text(receiver) + .subTitleFont() + .lineLimit(1) + } + } + .frame(maxWidth: 200, alignment: .trailing) // 최대 너비 200 + } } } .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 20) } + + // 총 텍스트의 너비를 계산하는 helper 함수 + private func totalTextWidth(_ participants: [String]) -> CGFloat { + // 전체 텍스트 길이를 대략적으로 계산합니다. + let text = participants.joined(separator: " ") + let font = UIFont.preferredFont(forTextStyle: .body) // 적절한 폰트를 사용 + let attributes: [NSAttributedString.Key: Any] = [.font: font] + let size = (text as NSString).size(withAttributes: attributes) + return size.width + } } struct requestDescriptionRow: View { diff --git a/noto-App/noto-App/Page/RequestList_Page.swift b/noto-App/noto-App/Page/RequestList_Page.swift index 67e60b8..1032d3a 100644 --- a/noto-App/noto-App/Page/RequestList_Page.swift +++ b/noto-App/noto-App/Page/RequestList_Page.swift @@ -1,93 +1,128 @@ import SwiftUI -struct RequestPage_ContentView: View { - @State private var currentScreen: Page = .requestList - @State private var rid: Int = 0 - @State private var type: Int = 1 - @State private var rejected: Int = 1 - var body: some View { - RequestListPage(currentScreen: $currentScreen, - selectedRid: $rid, - selectedRequestType: $type, - selectedRequestRejected: $rejected) - } -} - -struct RequestPage_ContentView_Preview: PreviewProvider { - static var previews: some View { - RequestPage_ContentView() - } -} +//struct RequestPage_ContentView: View { +// @State private var currentScreen: Page = .requestList +// @State private var rid: Int = 0 +// @State private var type: Int = 1 +// @State private var rejected: Int = 1 +// var body: some View { +// RequestListPage(currentScreen: $currentScreen, +// selectedRid: $rid, +// selectedRequestType: $type, +// selectedRequestRejected: $rejected) +// } +//} +// +//struct RequestPage_ContentView_Preview: PreviewProvider { +// static var previews: some View { +// RequestPage_ContentView() +// } +//} struct RequestListPage: View { + @State private var showingRequestList: Bool = false + @State private var showingRequest: Bool = false + @State private var selectedModal: Modal = .sendRequestList @Binding var currentScreen: Page - @Binding var selectedRid: Int - @Binding var selectedRequestType: Int - @Binding var selectedRequestRejected: Int + @Binding var selectedRid: Int + var prevScreen: Page + + @State private var requestListData: RequestList? = nil + + let url = "https://www.bestbirthday.co.kr:8080/api" var body: some View { VStack { ScrollView { - VStack(spacing: 15) { - dumyHeader() - requestHeader(onGoBack: {currentScreen = .main}, addAction: {print("리퀘스트 추가 버튼 클릭 (수정 필요)")}) - - // 보낸 일정 - Spacer() - titleRow_2(title: "보낸 리퀘스트", imageName: "paper-plane", imageSize: 20) - VStack(spacing: 5){ - Spacer() - ForEach(sentRequests.indices, id: \.self) { index in - HStack { - let request = sentRequests[index] - rowComponent(imageName: "mail", - title: request.title, - subtitle: request.subtitle, - action: { currentScreen = .requestDetail - selectedRid = request.rid - selectedRequestType = 1 - selectedRequestRejected = request.rejected}) - } - if index < sentRequests.count - 1 { - Divider() - .padding(.horizontal, 20) + VStack(spacing: 15) { + dumyHeader() + requestHeader(onGoBack: {currentScreen = prevScreen}, addAction: { + print("리퀘스트 추가 버튼 클릭 (수정 필요)") + showingRequest = true + }) + .sheet(isPresented: $showingRequest, onDismiss: { + showingRequest = false + }){ + modalRequestView() } - } - viewAllComponent(title: "보낸 리퀘스트 모두 보기", action: {print("보낸 리퀘스트 모두 보기 클릭 (수정 필요)")}) - } - .blockStyle(height: .infinity) - - // 받은 일정 - Spacer() - titleRow_2(title: "받은 리퀘스트", imageName: "receive", imageSize: 24) - VStack(spacing: 5){ - Spacer() - ForEach(receivedRequests.indices, id: \.self) { index in - HStack { - let request = receivedRequests[index] - rowComponent(imageName: "check", - title: request.title, - subtitle: request.subtitle, - action: { currentScreen = .requestDetail - selectedRid = request.rid - selectedRequestType = 2 - selectedRequestRejected = 0}) - } - if index < receivedRequests.count - 1 { - Divider() - .padding(.horizontal, 20) + + if let requestListData = requestListData{ + // 받은 일정 + Spacer() + titleRow_2(title: "받은 리퀘스트", imageName: "receive", imageSize: 24) + VStack(spacing: 5){ + Spacer() + ForEach(requestListData.receiveRequest.prefix(5), id: \.self) { receiveRequest in + HStack { + rowComponent(imageName: "mail", + title: receiveRequest.title, + subtitle: receiveRequest.sender, + action: { + currentScreen = .requestDetail + selectedRid = receiveRequest.requestId + }) + } + if receiveRequest != requestListData.receiveRequest.prefix(5).last { + Divider() + .padding(.horizontal, 20) + } + } + viewAllComponent(title: "받은 리퀘스트 모두 보기", action: { + showingRequestList = true + selectedModal = .receiveRequestList + }) + } + .blockStyle(height: .infinity) + + // 보낸 일정 + Spacer() + titleRow_2(title: "보낸 리퀘스트", imageName: "paper-plane", imageSize: 20) + VStack(spacing: 5){ + Spacer() + ForEach(requestListData.sendRequest.prefix(5), id: \.self) { sendRequest in + HStack { + rowComponent(imageName: "mail", + title: sendRequest.title, + subtitle: sendRequest.sender, + action: { + currentScreen = .requestDetail + selectedRid = sendRequest.requestId + }) + } + if sendRequest != requestListData.sendRequest.prefix(5).last { + Divider() + .padding(.horizontal, 20) + } + } + viewAllComponent(title: "보낸 리퀘스트 모두 보기", action: { + showingRequestList = true + selectedModal = .sendRequestList + }) + } + .blockStyle(height: .infinity) } - } - viewAllComponent(title: "받은 리퀘스트 모두 보기", action: {print("받은 리퀘스트 모두 보기 클릭 (수정 필요)")}) + dumyBottom() } - .blockStyle(height: .infinity) - - dumyBottom() } .backgroundStyle() } .scrollViewStyle() + .task { + do { + let requestListResponse = try await get(url: url + "/request/list?userId=1", responseType: RequestList.self) + // Handle the response here + print(requestListResponse) + + requestListData = requestListResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + .sheet(isPresented: $showingRequestList, onDismiss: { + showingRequestList = false + }){ + modalRequestListView(requestListData: $requestListData, selectedModal: $selectedModal, currentScreen: $currentScreen, selectedRid: $selectedRid) + } } - } } diff --git a/noto-App/noto-App/Page/Setting_Page.swift b/noto-App/noto-App/Page/Setting_Page.swift index 7c50fd1..53e7068 100644 --- a/noto-App/noto-App/Page/Setting_Page.swift +++ b/noto-App/noto-App/Page/Setting_Page.swift @@ -1,32 +1,32 @@ import SwiftUI // inner는 추후 삭제 -enum Screen { case settings, user, pw, alarm, display, client, faq, info } +//enum Screen { case settings, user, pw, alarm, display, client, faq, info } -struct SettingPage_ContentView: View { - @State private var currentScreen: Screen = .settings - - var body: some View { - VStack { - if currentScreen == .settings { - SettingPage() - } else if currentScreen == .user { - - } - } - } -} - -struct SettingPage_ContentView_Preview: PreviewProvider { - static var previews: some View { - SettingPage_ContentView() - } -} +//struct SettingPage_ContentView: View { +// @State private var currentScreen: Screen = .settings +// +// var body: some View { +// VStack { +// if currentScreen == .settings { +// SettingPage(currentScreen: $currentScreen) +// } else if currentScreen == .user { +// +// } +// } +// } +//} +// +//struct SettingPage_ContentView_Preview: PreviewProvider { +// static var previews: some View { +// SettingPage_ContentView() +// } +//} struct SettingPage: View { @State private var searchText = "" - @State private var currentScreen: Screen = .settings - private var personIndex: Int = 0 + @Binding var currentScreen: Page + var personIndex: Int = 0 var body: some View { VStack(alignment: .leading) { @@ -69,6 +69,9 @@ struct SettingPage: View { .backgroundStyle() } .scrollViewStyle() + .onAppear { + currentScreen = .settings + } } } } diff --git a/noto-App/noto-App/Page/TodoDetail_Page.swift b/noto-App/noto-App/Page/TodoDetail_Page.swift index 2c4b3ea..2eedf43 100644 --- a/noto-App/noto-App/Page/TodoDetail_Page.swift +++ b/noto-App/noto-App/Page/TodoDetail_Page.swift @@ -1,24 +1,29 @@ import SwiftUI -struct TodoDetailPage_ContentView: View { - @State private var currentScreen: Page = .todoDetail - @State private var prevScreen: Page = .main - @State private var clickedIndex: Int = 1 - var body: some View { - TodoDetialPage(currentScreen: $currentScreen, prevScreen: prevScreen, index: clickedIndex) - } -} - -struct TodoDetailPage_ContentView_Preview: PreviewProvider { - static var previews: some View { - TodoDetailPage_ContentView() - } -} +//struct TodoDetailPage_ContentView: View { +// @State private var currentScreen: Page = .todoDetail +// @State private var prevScreen: Page = .main +// @State private var clickedIndex: Int = 1 +// var body: some View { +// TodoDetialPage(currentScreen: $currentScreen, prevScreen: prevScreen, index: clickedIndex) +// } +//} +// +//struct TodoDetailPage_ContentView_Preview: PreviewProvider { +// static var previews: some View { +// TodoDetailPage_ContentView() +// } +//} struct TodoDetialPage: View { + @State private var scheduleInfoData: ScheduleInfo? = nil + @State private var showingSheet: Bool = false @Binding var currentScreen: Page + @Binding var selectedSid: Int + @Binding var selectedPid: Int var prevScreen: Page - var index: Int + + let url = "https://www.bestbirthday.co.kr:8080/api" var body: some View { VStack { @@ -26,47 +31,94 @@ struct TodoDetialPage: View { VStack(spacing: 15) { dumyHeader() goBackCompletionHeader(goBackAction: {currentScreen = prevScreen}, - completionAction: { print("일정 완료 버튼 클릭 (수정 필요)") }) - - VStack(spacing: 10){ - Spacer() - titleRow_3(title: todolist[index].title, optionAction: {print("일정 수정 버튼 클릭 (수정 필요)")}) - .padding(.top, 10) - Divider() - .padding(.horizontal, 20) - .padding(.bottom, 10) - - VStack(spacing: 15) { - dateRow(startDate: todolist[index].startDate, endDate: todolist[index].endDate) - participantRow() - tagRow(tag: todolist[index].tag) - urlRow(url: todolist[index].url) - Divider() - .padding(.horizontal, 20) - descriptionRow(description: todolist[index].subtitle) + completionAction: { + print("일정 완료 버튼 클릭 (수정 필요)") + Task { + do { + let response = try await putNoneBody(url: url + "/schedule/complete" + "/\(selectedSid)") + // Handle the response here + print(response) + + Task { + do { + let response = try await delete(url: url + "/schedule" + "/\(selectedSid)") + // Handle the response here + print(response) + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } + }) + if let scheduleInfoData = scheduleInfoData { + VStack(spacing: 10){ + Spacer() + titleRow_3(title: scheduleInfoData.name, optionAction: { + print("일정 수정 버튼 클릭 (수정 필요)") + showingSheet = true + }) + .padding(.top, 10) + .sheet(isPresented: $showingSheet, onDismiss: { + showingSheet = false + }) { + modalTodoView(modifiedSid: $selectedSid, selectedPid: $selectedPid) + } + Divider() + .padding(.horizontal, 20) + .padding(.bottom, 10) + + VStack(spacing: 15) { + dateRow(startDate: scheduleInfoData.startDate, endDate: scheduleInfoData.endDate) + participantRow(participants: scheduleInfoData.participants) + tagRow(tag: scheduleInfoData.tag) + urlRow(url: scheduleInfoData.url) + Divider() + .padding(.horizontal, 20) + descriptionRow(description: scheduleInfoData.description) + } + Spacer() + } + .blockStyle(height: .infinity) + + VStack { + urlButton(url: scheduleInfoData.url) + } + .blockStyle(height: .infinity) + .padding(.top, 20) + .padding(.horizontal, 5) } - Spacer() - } - .blockStyle(height: .infinity) - - VStack { - urlButton(url: todolist[index].url) - } - .blockStyle(height: .infinity) - .padding(.top, 20) - .padding(.horizontal, 5) } .backgroundStyle() } .scrollViewStyle() + .task { + do { + let scheduleInfoResponse = try await get(url: url + "/schedule/info" + "/\(selectedSid)", responseType: ScheduleInfo.self) + // Handle the response here + print(scheduleInfoResponse) + + scheduleInfoData = scheduleInfoResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } } } } struct dateRow: View { - var startDate: Date - var endDate: Date + let startDate: Date + let endDate: Date + + private var dateFormatter: DateFormatter { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd" + return formatter + } var body: some View { HStack { @@ -102,6 +154,8 @@ struct dateRow: View { } struct participantRow: View { + let participants: [String] + var body: some View { HStack { Image("user") @@ -111,27 +165,52 @@ struct participantRow: View { Text("일정 참가자") .subTitleFont() - Spacer() - - HStack { - ForEach(personList.indices, id: \.self) { index in - VStack{ - imageComponent(imageName: personList[index].imageName, shape: .rectangle, size: 24) - } - - if index < personList.count - 1 { - Divider() - } + Spacer() + + // ScrollView를 추가하여 가로로 스크롤 할 수 있도록 합니다. + if totalTextWidth(participants) > 200 { + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 8) { + // 텍스트들 사이에 간격을 추가합니다. + ForEach(participants, id: \.self) { participant in + Text(participant) + .projectContentFont() + .lineLimit(1) + } + } + .frame(maxWidth: .infinity, alignment: .trailing) // 오른쪽 정렬 + } + .frame(maxWidth: 200) // 최대 너비 200 + } else { + // 텍스트가 200 이하일 경우, 오른쪽 정렬 + HStack(spacing: 8) { + Spacer() + ForEach(participants, id: \.self) { participant in + Text(participant) + .projectContentFont() + .lineLimit(1) + } + } + .frame(maxWidth: 200, alignment: .trailing) // 최대 너비 200 } - } } .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 20) } + + // 총 텍스트의 너비를 계산하는 helper 함수 + private func totalTextWidth(_ participants: [String]) -> CGFloat { + // 전체 텍스트 길이를 대략적으로 계산합니다. + let text = participants.joined(separator: " ") + let font = UIFont.preferredFont(forTextStyle: .body) // 적절한 폰트를 사용 + let attributes: [NSAttributedString.Key: Any] = [.font: font] + let size = (text as NSString).size(withAttributes: attributes) + return size.width + } } struct tagRow: View { - var tag: Int + let tag: String var body: some View { HStack { @@ -145,7 +224,7 @@ struct tagRow: View { Spacer() HStack { - if tag == 0 { + if tag == "개발" { ZStack{ Rectangle() .fill(Color.customLightGray) @@ -165,7 +244,7 @@ struct tagRow: View { .foregroundColor(.white) .subTitleFont() } - } else if tag == 1 { + } else if tag == "회의" { ZStack{ Rectangle() .fill(Color.customBlue) @@ -194,7 +273,7 @@ struct tagRow: View { } struct urlRow: View { - var url: String + let url: String var body: some View { HStack { Image("chain") @@ -207,17 +286,36 @@ struct urlRow: View { Spacer() - HStack{ - Text(url) - .font(.custom("Freesentation-5Medium", size: 12)) - .foregroundColor(.customBlack) - .lineLimit(1) - .truncationMode(.tail) - } + // URL 텍스트의 길이가 200을 넘으면 ScrollView를 사용하여 스크롤 가능하게 처리 + if urlWidth(url) > 200 { + ScrollView(.horizontal, showsIndicators: false) { + Text(url) + .font(.custom("Freesentation-5Medium", size: 12)) + .foregroundColor(.customBlack) + .lineLimit(1) + .truncationMode(.tail) + } + .frame(maxWidth: 200, alignment: .leading) // 최대 너비를 200으로 제한 + } else { + Text(url) + .font(.custom("Freesentation-5Medium", size: 12)) + .foregroundColor(.customBlack) + .lineLimit(1) + .truncationMode(.tail) + .frame(maxWidth: 200, alignment: .leading) // 텍스트가 200 이하일 때 왼쪽 정렬 + } } .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 20) } + + // URL 텍스트의 너비를 계산하는 함수 + private func urlWidth(_ url: String) -> CGFloat { + let font = UIFont(name: "Freesentation-5Medium", size: 12) ?? UIFont.systemFont(ofSize: 12) + let attributes: [NSAttributedString.Key: Any] = [.font: font] + let size = (url as NSString).size(withAttributes: attributes) + return size.width + } } struct descriptionRow: View { From e059b06f1439ff6d7fc099968237e9cfc929eca0 Mon Sep 17 00:00:00 2001 From: SanghyeonPark Date: Fri, 13 Dec 2024 20:28:51 +0900 Subject: [PATCH 2/2] combine - complete woooooooowwwwwww --- noto-App/noto-App/Api/Post_Api.swift | 12 ++-- .../Contents.json" | 2 +- .../KakaoTalk_Photo_2024-12-13-17-11-58.png" | Bin .../noto-App/Component/Modal_Component.swift | 55 +++++++++++++----- noto-App/noto-App/Page/Main_Page.swift | 4 +- noto-App/noto-App/Page/Project_Page.swift | 14 ++--- .../noto-App/Page/RequestDetail_Page.swift | 5 +- noto-App/noto-App/Page/RequestList_Page.swift | 12 ++++ noto-App/noto-App/Page/TodoDetail_Page.swift | 17 ++---- 9 files changed, 81 insertions(+), 40 deletions(-) rename "noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/Noto \353\241\234\352\263\240.png" => "noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/KakaoTalk_Photo_2024-12-13-17-11-58.png" (100%) diff --git a/noto-App/noto-App/Api/Post_Api.swift b/noto-App/noto-App/Api/Post_Api.swift index 7cb21bd..bf6dd24 100644 --- a/noto-App/noto-App/Api/Post_Api.swift +++ b/noto-App/noto-App/Api/Post_Api.swift @@ -24,11 +24,11 @@ func post(url: String, body: U, responseType: T.Type) as let encoder = JSONEncoder() // 날짜 포맷 설정 - let formatter = DateFormatter() - formatter.dateFormat = "yyyy-MM-dd" // 서버에서 요구하는 날짜 형식으로 설정 + let formatter0 = DateFormatter() + formatter0.dateFormat = "yyyy-MM-dd" // 서버에서 요구하는 날짜 형식으로 설정 // JSONEncoder에 날짜 인코딩 전략 설정 - encoder.dateEncodingStrategy = .formatted(formatter) + encoder.dateEncodingStrategy = .formatted(formatter0) let bodyData = try encoder.encode(body) request.httpBody = bodyData @@ -43,9 +43,13 @@ func post(url: String, body: U, responseType: T.Type) as // Create a JSON decoder let decoder = JSONDecoder() + + // Custom DateFormatter for the expected date format + let formatter1 = DateFormatter() + formatter1.dateFormat = "yyyy-MM-dd" // Specify your custom date format here // Set the custom formatter to the decoder - decoder.dateDecodingStrategy = .formatted(formatter) + decoder.dateDecodingStrategy = .formatted(formatter1) // Decode the API response let apiResponse = try decoder.decode(ApiModel.self, from: data) diff --git "a/noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/Contents.json" "b/noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/Contents.json" index d63c8d2..4e6296c 100644 --- "a/noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/Contents.json" +++ "b/noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/Contents.json" @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Noto 로고.png", + "filename" : "KakaoTalk_Photo_2024-12-13-17-11-58.png", "idiom" : "universal", "scale" : "1x" }, diff --git "a/noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/Noto \353\241\234\352\263\240.png" "b/noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/KakaoTalk_Photo_2024-12-13-17-11-58.png" similarity index 100% rename from "noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/Noto \353\241\234\352\263\240.png" rename to "noto-App/noto-App/Assets.xcassets/Noto \353\241\234\352\263\240.imageset/KakaoTalk_Photo_2024-12-13-17-11-58.png" diff --git a/noto-App/noto-App/Component/Modal_Component.swift b/noto-App/noto-App/Component/Modal_Component.swift index b263851..14887f1 100644 --- a/noto-App/noto-App/Component/Modal_Component.swift +++ b/noto-App/noto-App/Component/Modal_Component.swift @@ -71,7 +71,7 @@ struct modalTodoListView: View { .sheet(isPresented: $showingSheet, onDismiss: { showingSheet = false }){ - modalTodoView(modifiedSid: $modifiedSid, selectedPid: $selectedPid) + modalTodoView(currentScreen: $currentScreen, modifiedSid: $modifiedSid, selectedPid: $selectedPid, selectedSid: $selectedSid) } Button("일정 삭제", role: .destructive) { Task { @@ -115,10 +115,13 @@ struct modalTodoListView: View { .sheet(isPresented: $showingSheet, onDismiss: { showingSheet = false }){ - modalTodoView(modifiedSid: $modifiedSid, selectedPid: $selectedPid) + modalTodoView(currentScreen: $currentScreen, modifiedSid: $modifiedSid, selectedPid: $selectedPid, selectedSid: $selectedSid) } } .modalPresentation() + .onChange(of: currentScreen) { + dismiss() + } } } @@ -287,7 +290,7 @@ struct modalProjectListView: View { .sheet(isPresented: $showingSheet, onDismiss: { showingSheet = false }){ - modalProjectView(modifiedPid: $modifiedPid) + modalProjectView(modifiedPid: $modifiedPid, currentScreen: $currentScreen, selectedPid: $selectedPid) } Button("프로젝트 삭제", role: .destructive) { Task { @@ -331,7 +334,7 @@ struct modalProjectListView: View { .sheet(isPresented: $showingSheet, onDismiss: { showingSheet = false }){ - modalProjectView(modifiedPid: $modifiedPid) + modalProjectView(modifiedPid: $modifiedPid, currentScreen: $currentScreen, selectedPid: $selectedPid) } } .modalPresentation() @@ -346,6 +349,9 @@ struct modalProjectListView: View { print("Error fetching data: \(error.localizedDescription)") } } + .onChange(of: currentScreen) { + dismiss() + } } } @@ -363,8 +369,10 @@ struct modalTodoView: View { @State private var url: String = "" @State private var isParticipantsListVisible: Bool = false // 추가된 상태 변수 + @Binding var currentScreen: Page @Binding var modifiedSid: Int @Binding var selectedPid: Int + @Binding var selectedSid: Int // 완료 버튼 활성화 조건 private var isCompleteButtonEnabled: Bool { @@ -390,12 +398,20 @@ struct modalTodoView: View { if modifiedSid == 0 { let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/schedule", body: RequestSchedule(projectId: selectedPid, name: name, startDate: startDate, endDate: endDate, participants: participants, description: description, tag: tag, url: url), responseType: ScheduleInfo.self) print(response) + scheduleInfoData = response.data } else { let response = try await put(url: "https://www.bestbirthday.co.kr:8080/api/schedule" + "/\(modifiedSid)", body: RequestSchedule(projectId: selectedPid, name: name, startDate: startDate, endDate: endDate, participants: participants, description: description, tag: tag, url: url), responseType: ScheduleInfo.self) print(response) } dismiss() + + if let scheduleInfoData = scheduleInfoData { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { + currentScreen = .todoDetail + selectedSid = scheduleInfoData.id + } + } } catch { if modifiedSid == 0 { print("일정 생성 실패: \(error.localizedDescription)") @@ -581,6 +597,8 @@ struct modalProjectView: View { @State private var projectData: Project? = nil @Binding var modifiedPid: Int + @Binding var currentScreen: Page + @Binding var selectedPid: Int // 완료 버튼 활성화 조건 private var isCompleteButtonEnabled: Bool { @@ -614,6 +632,13 @@ struct modalProjectView: View { let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/user/participate", body: RequestUserParticipate(userId: 1, projectId: projectData.id), responseType: String?.self) print(response) + // 두 번째 모달을 닫음 + dismiss() + + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.001) { + currentScreen = .projectDetail + selectedPid = projectData.id + } } } catch { print("유저 참가 실패: \(error.localizedDescription)") @@ -759,15 +784,15 @@ struct modalRequestView: View { .frame(maxWidth: .infinity, alignment: .leading) Button(action: { + print("리퀘스트 전송") Task { do { -// let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/request" + "?projectId=\(projectId)" + "&scheduleId=\(scheduleId)" + "&receivers=string&sender=0&title=string&content=string", body: RequestSchedule(projectId: selectedPid, name: name, startDate: startDate, endDate: endDate, participants: participants, description: description, tag: tag, url: url), responseType: ScheduleInfo.self) -// print(response) - - dismiss() + let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/request", body: RequestRequest(projectId: projectId, scheduleId: scheduleId, receivers: receivers, sender: sender, title: title, content: content), responseType: RequestInfo.self) + print(response) } catch { print("리퀘스트 전송 실패: \(error.localizedDescription)") } + dismiss() } }) { Text("전송") @@ -786,7 +811,7 @@ struct modalRequestView: View { }) { Text("프로젝트 선택") .foregroundColor(.black) - .frame(maxWidth: 100, maxHeight: 40) // 크기 설정 + .frame(maxWidth: 350, maxHeight: 40) // 크기 설정 .background(Color.white) // 배경색 설정 .cornerRadius(8) // 둥근 모서리 .shadow(radius: 5) // 약간의 그림자 효과 @@ -838,7 +863,7 @@ struct modalRequestView: View { }) { Text("일정 선택") .foregroundColor(.black) - .frame(maxWidth: 100, maxHeight: 40) // 크기 설정 + .frame(maxWidth: 350, maxHeight: 40) // 크기 설정 .background(Color.white) // 배경색 설정 .cornerRadius(8) // 둥근 모서리 .shadow(radius: 5) // 약간의 그림자 효과 @@ -901,7 +926,7 @@ struct modalRequestView: View { }) { Text("수신자 선택") .foregroundColor(.black) - .frame(maxWidth: 100, maxHeight: 40) // 크기 설정 + .frame(maxWidth: 350, maxHeight: 40) // 크기 설정 .background(Color.white) // 배경색 설정 .cornerRadius(8) // 둥근 모서리 .shadow(radius: 5) // 약간의 그림자 효과 @@ -1019,7 +1044,10 @@ struct modalRequestView: View { // 리퀘스트 거부 모달 컴포넌트 struct modalRequestRejectView: View { @Environment(\.dismiss) var dismiss + @Binding var receiverId: Int @Binding var comment: String + @Binding var status: String + @Binding var selectedRid: Int // 완료 버튼 활성화 조건 private var isCompleteButtonEnabled: Bool { @@ -1034,10 +1062,11 @@ struct modalRequestRejectView: View { .frame(maxWidth: .infinity, alignment: .leading) Button(action: { + status = "거절" Task { do { -// let response = try await post(url: "https://www.bestbirthday.co.kr:8080/api/request" + "?projectId=\(projectId)" + "&scheduleId=\(scheduleId)" + "&receivers=string&sender=0&title=string&content=string", body: RequestSchedule(projectId: selectedPid, name: name, startDate: startDate, endDate: endDate, participants: participants, description: description, tag: tag, url: url), responseType: ScheduleInfo.self) -// print(response) + let response = try await patch(url: "https://www.bestbirthday.co.kr:8080/api/request" + "/\(selectedRid)", body: RequestRequestReceived(receiverId: receiverId, comment: comment, status: status)) + print(response) dismiss() } catch { diff --git a/noto-App/noto-App/Page/Main_Page.swift b/noto-App/noto-App/Page/Main_Page.swift index 165c315..c8615da 100644 --- a/noto-App/noto-App/Page/Main_Page.swift +++ b/noto-App/noto-App/Page/Main_Page.swift @@ -205,8 +205,8 @@ struct mainPage: View { if let progress = projectProgress.progress{ simpleProgressRow(title: projectProgress.name, progress: progress, - Dday: -projectProgress.dday, - action: { currentScreen = .projectDetail }) + Dday: projectProgress.dday, + action: {}) } } } diff --git a/noto-App/noto-App/Page/Project_Page.swift b/noto-App/noto-App/Page/Project_Page.swift index 68e5bda..916c8cb 100644 --- a/noto-App/noto-App/Page/Project_Page.swift +++ b/noto-App/noto-App/Page/Project_Page.swift @@ -59,7 +59,7 @@ struct ProjectPage: View { .sheet(isPresented: $showingModalProject, onDismiss: { showingModalProject = false }) { - modalProjectView(modifiedPid: $selectedPid) + modalProjectView(modifiedPid: $selectedPid, currentScreen: $currentScreen, selectedPid: $selectedPid) } Divider() .padding(10) @@ -299,12 +299,12 @@ struct CalendarGridView: View { // 특정 날짜에 이벤트가 있는지 확인 private func eventForDate(_ date: Date) -> Todo? { - for todo in todolist { - if calendar.isDate(date, inSameDayAs: todo.startDate) || - (todo.startDate...todo.endDate).contains(date) { - return todo - } - } +// for todo in todolist { +// if calendar.isDate(date, inSameDayAs: todo.startDate) || +// (todo.startDate...todo.endDate).contains(date) { +// return todo +// } +// } return nil } diff --git a/noto-App/noto-App/Page/RequestDetail_Page.swift b/noto-App/noto-App/Page/RequestDetail_Page.swift index dddae71..7050e5a 100644 --- a/noto-App/noto-App/Page/RequestDetail_Page.swift +++ b/noto-App/noto-App/Page/RequestDetail_Page.swift @@ -32,7 +32,7 @@ struct RequestDetailPage: View { @State private var showingSheet: Bool = false @State private var showingDialog: Bool = false - @State private var receiverId: Int = 0 + @State private var receiverId: Int = 1 @State private var comment: String = "" @State private var status: String = "" @@ -66,6 +66,7 @@ struct RequestDetailPage: View { Button("수락", role: .none) { print("수락") status = "수락" + print("\(selectedRid)") Task { let response = try await patch(url: url + "/request" + "/\(selectedRid)", body: RequestRequestReceived(receiverId: receiverId, comment: comment, status: status)) // Handle the response here @@ -83,7 +84,7 @@ struct RequestDetailPage: View { } .padding(.top, 10) .sheet(isPresented: $showingSheet) { - modalRequestRejectView(comment: $comment) + modalRequestRejectView(receiverId: $receiverId, comment: $comment, status: $status, selectedRid: $selectedRid) } } else { titleRow_1(title: title) diff --git a/noto-App/noto-App/Page/RequestList_Page.swift b/noto-App/noto-App/Page/RequestList_Page.swift index 1032d3a..4be02fc 100644 --- a/noto-App/noto-App/Page/RequestList_Page.swift +++ b/noto-App/noto-App/Page/RequestList_Page.swift @@ -43,6 +43,18 @@ struct RequestListPage: View { }) .sheet(isPresented: $showingRequest, onDismiss: { showingRequest = false + + Task { + do { + let requestListResponse = try await get(url: url + "/request/list?userId=1", responseType: RequestList.self) + // Handle the response here + print(requestListResponse) + + requestListData = requestListResponse.data + } catch { + print("Error fetching data: \(error.localizedDescription)") + } + } }){ modalRequestView() } diff --git a/noto-App/noto-App/Page/TodoDetail_Page.swift b/noto-App/noto-App/Page/TodoDetail_Page.swift index 2eedf43..0fa71de 100644 --- a/noto-App/noto-App/Page/TodoDetail_Page.swift +++ b/noto-App/noto-App/Page/TodoDetail_Page.swift @@ -16,6 +16,7 @@ import SwiftUI //} struct TodoDetialPage: View { + @Environment(\.dismiss) var dismiss @State private var scheduleInfoData: ScheduleInfo? = nil @State private var showingSheet: Bool = false @Binding var currentScreen: Page @@ -38,16 +39,7 @@ struct TodoDetialPage: View { let response = try await putNoneBody(url: url + "/schedule/complete" + "/\(selectedSid)") // Handle the response here print(response) - - Task { - do { - let response = try await delete(url: url + "/schedule" + "/\(selectedSid)") - // Handle the response here - print(response) - } catch { - print("Error fetching data: \(error.localizedDescription)") - } - } + currentScreen = prevScreen } catch { print("Error fetching data: \(error.localizedDescription)") } @@ -64,7 +56,7 @@ struct TodoDetialPage: View { .sheet(isPresented: $showingSheet, onDismiss: { showingSheet = false }) { - modalTodoView(modifiedSid: $selectedSid, selectedPid: $selectedPid) + modalTodoView(currentScreen: $currentScreen, modifiedSid: $selectedSid, selectedPid: $selectedPid, selectedSid: $selectedSid) } Divider() .padding(.horizontal, 20) @@ -106,6 +98,9 @@ struct TodoDetialPage: View { print("Error fetching data: \(error.localizedDescription)") } } + .onChange(of: currentScreen) { + dismiss() + } } } }