Test
This commit is contained in:
parent
b02518850d
commit
be42ea7176
@ -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>
|
||||||
@ -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>
|
||||||
@ -1,24 +1,244 @@
|
|||||||
//
|
// ContentView.swift - Main Screen
|
||||||
// ContentView.swift
|
|
||||||
// PanicButton
|
|
||||||
//
|
|
||||||
// Created by Dendi Yogia Pratama on 27/11/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ContentView: View {
|
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 {
|
var body: some View {
|
||||||
VStack {
|
NavigationView {
|
||||||
Image(systemName: "globe")
|
ScrollView {
|
||||||
.imageScale(.large)
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
.foregroundStyle(.tint)
|
Text("Terjadi Kondisi Darurat")
|
||||||
Text("Hello, world!")
|
.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()
|
.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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
// NetworkManager.swift - HTTP Request Handler
|
||||||
ContentView()
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user