From be42ea7176b83778cb13c23e444c899c76931514 Mon Sep 17 00:00:00 2001 From: dend <2023010715051@mhs.ubharajaya.ac.id> Date: Thu, 27 Nov 2025 15:09:27 +0700 Subject: [PATCH] Test --- .../xcshareddata/WorkspaceSettings.xcsettings | 5 + .../WorkspaceSettings.xcsettings | 16 ++ PanicButton/ContentView.swift | 250 ++++++++++++++++-- 3 files changed, 256 insertions(+), 15 deletions(-) create mode 100644 PanicButton.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 PanicButton.xcodeproj/project.xcworkspace/xcuserdata/dend.xcuserdatad/WorkspaceSettings.xcsettings diff --git a/PanicButton.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/PanicButton.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/PanicButton.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/PanicButton.xcodeproj/project.xcworkspace/xcuserdata/dend.xcuserdatad/WorkspaceSettings.xcsettings b/PanicButton.xcodeproj/project.xcworkspace/xcuserdata/dend.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..723a561 --- /dev/null +++ b/PanicButton.xcodeproj/project.xcworkspace/xcuserdata/dend.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,16 @@ + + + + + BuildLocationStyle + UseAppPreferences + CompilationCachingSetting + Default + CustomBuildLocationType + RelativeToDerivedData + DerivedDataLocationStyle + Default + ShowSharedSchemesAutomaticallyEnabled + + + diff --git a/PanicButton/ContentView.swift b/PanicButton/ContentView.swift index be70ff5..87d8965 100644 --- a/PanicButton/ContentView.swift +++ b/PanicButton/ContentView.swift @@ -1,24 +1,244 @@ -// -// ContentView.swift -// PanicButton -// -// Created by Dendi Yogia Pratama on 27/11/25. -// - +// ContentView.swift - Main Screen import SwiftUI struct ContentView: View { + @State private var message = "Klik tombol untuk mengirim notifikasi" + @State private var selectedConditions: Set = [] + @State private var additionalNotes = "" + @State private var showEvacuationRoute = false + @FocusState private var isNotesFieldFocused: Bool + + let conditions = [ + "🔥 Kebakaran", + "⛈️ Banjir", + "🌊 Tsunami", + "🌋 Gunung Meletus", + "🌏 Gempa Bumi", + "👿 Huru hara", + "🐍 Binatang Buas", + "☢️ Radiasi Nuklir", + "☣️ Biohazard" + ] + var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") + NavigationView { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + Text("Terjadi Kondisi Darurat") + .font(.system(size: 20, weight: .bold)) + .foregroundColor(.red) + .padding(.bottom, 8) + + // Daftar kondisi dengan checkbox + ForEach(conditions, id: \.self) { condition in + HStack { + Button(action: { + isNotesFieldFocused = false + toggleCondition(condition) + }) { + HStack { + Image(systemName: selectedConditions.contains(condition) ? "checkmark.square.fill" : "square") + .foregroundColor(selectedConditions.contains(condition) ? .blue : .gray) + Text(condition) + .foregroundColor(.primary) + } + } + Spacer() + } + .padding(.vertical, 4) + .contentShape(Rectangle()) + .onTapGesture { + isNotesFieldFocused = false + toggleCondition(condition) + } + } + + Spacer().frame(height: 16) + + Text("Catatan tambahan:") + .font(.system(size: 16)) + + ZStack(alignment: .topLeading) { + TextEditor(text: $additionalNotes) + .frame(height: 100) + .padding(8) + .scrollContentBackground(.hidden) + .background(Color(.systemBackground)) + .cornerRadius(4) + .overlay( + RoundedRectangle(cornerRadius: 4) + .stroke(Color.gray, lineWidth: 1) + ) + .focused($isNotesFieldFocused) + + // Placeholder text + if additionalNotes.isEmpty { + Text("Tulis catatan di sini...") + .foregroundColor(Color(.placeholderText)) + .padding(.horizontal, 12) + .padding(.vertical, 16) + .allowsHitTesting(false) + } + } + + Spacer().frame(height: 16) + + Button(action: { + isNotesFieldFocused = false + sendReport() + }) { + Text("Kirim Laporan") + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color.red) + .cornerRadius(8) + } + + Spacer().frame(height: 16) + + Text("\"JANGAN PANIK! SEGERA EVAKUASI\nDIRI ANDA KE TITIK KUMPUL\"") + .foregroundColor(.red) + .font(.system(size: 15, weight: .medium)) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity) + + Spacer().frame(height: 16) + + Text(message) + .padding(.top, 16) + + Button(action: { + isNotesFieldFocused = false + showEvacuationRoute = true + }) { + Text("Lihat Jalur Evakuasi") + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color.green) + .cornerRadius(8) + } + } + .padding(16) + } + .navigationBarHidden(true) + .sheet(isPresented: $showEvacuationRoute) { + EvacuationRouteView() + } + } + } + + func toggleCondition(_ condition: String) { + if selectedConditions.contains(condition) { + selectedConditions.remove(condition) + } else { + selectedConditions.insert(condition) + } + } + + func sendReport() { + let conditions = Array(selectedConditions).joined(separator: ", ") + let report = "Kondisi: \(conditions)\nCatatan: \(additionalNotes)" + + sendNotification(condition: conditions, report: report) { response in + DispatchQueue.main.async { + self.message = response + } } - .padding() } } -#Preview { - ContentView() +// NetworkManager.swift - HTTP Request Handler +func sendNotification(condition: String, report: String, completion: @escaping (String) -> Void) { + let url = URL(string: "https://ntfy.ubharajaya.ac.id/panic-button")! + + let tagMapping: [String: String] = [ + "🔥 Kebakaran": "fire", + "⛈️ Banjir": "cloud_with_lightning_and_rain", + "🌊 Tsunami": "ocean", + "🌋 Gunung Meletus": "volcano", + "🌏 Gempa Bumi": "earth_asia", + "👿 Huru hara": "imp", + "🐍 Binatang Buas": "snake", + "☢️ Radiasi Nuklir": "radioactive", + "☣️ Biohazard": "biohazard" + ] + + let selectedList = condition + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespaces) } + .filter { !$0.isEmpty } + + let cleanConditionText = selectedList.joined(separator: ", ") + let emojiTags = selectedList.compactMap { tagMapping[String($0)] } + let finalTags = ["Alert"] + emojiTags + + let notesPart = report.components(separatedBy: "Catatan:").last ?? "" + let finalReport = "Kondisi: \(cleanConditionText)\nCatatan:\(notesPart)" + + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("Alert", forHTTPHeaderField: "Title") + request.setValue("urgent", forHTTPHeaderField: "Priority") + request.setValue(finalTags.joined(separator: ","), forHTTPHeaderField: "Tags") + request.setValue("text/plain", forHTTPHeaderField: "Content-Type") + request.httpBody = finalReport.data(using: .utf8) + + let task = URLSession.shared.dataTask(with: request) { data, response, error in + if let error = error { + completion("Error: \(error.localizedDescription)") + return + } + + if let httpResponse = response as? HTTPURLResponse { + if httpResponse.statusCode == 200 { + completion("Notifikasi berhasil dikirim!") + } else { + completion("Gagal mengirim notifikasi: \(httpResponse.statusCode)") + } + } + } + + task.resume() +} + +// EvacuationRouteView.swift - Evacuation Route Screen +struct EvacuationRouteView: View { + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + Text("Jalur Evakuasi") + .font(.system(size: 20, weight: .bold)) + .padding(.top, 16) + + Spacer().frame(height: 16) + + // Tampilkan gambar jalur evakuasi + // Ganti "jalur_evakuasi" dengan nama file gambar Anda + Image("jalur_evakuasi") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxWidth: .infinity) + .frame(height: 450) + .padding() + + Spacer().frame(height: 20) + + Button(action: { + dismiss() + }) { + Text("Close") + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color.red) + .cornerRadius(8) + } + .padding(.horizontal, 16) + + Spacer() + } + } }