This commit is contained in:
dend 2025-11-27 15:09:27 +07:00
parent b02518850d
commit be42ea7176
3 changed files with 256 additions and 15 deletions

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildLocationStyle</key>
<string>UseAppPreferences</string>
<key>CompilationCachingSetting</key>
<string>Default</string>
<key>CustomBuildLocationType</key>
<string>RelativeToDerivedData</string>
<key>DerivedDataLocationStyle</key>
<string>Default</string>
<key>ShowSharedSchemesAutomaticallyEnabled</key>
<true/>
</dict>
</plist>

View File

@ -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<String> = []
@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)
}
.padding()
}
Spacer()
}
.padding(.vertical, 4)
.contentShape(Rectangle())
.onTapGesture {
isNotesFieldFocused = false
toggleCondition(condition)
}
}
#Preview {
ContentView()
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
}
}
}
}
// 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()
}
}
}