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()
+ }
+ }
}