Perbaiki .gitignore agar log dan file sensitif tidak ikut commit

This commit is contained in:
WD - Dev
2025-10-18 16:05:45 +07:00
parent eb13c69431
commit 6017dfd369
27 changed files with 89223 additions and 280 deletions
+502 -78
View File
@@ -1,5 +1,12 @@
<!-- Main content -->
<section class="content">
<style>
/* Sembunyikan semua dulu saat pertama kali load */
.official-section,
.unofficial-section {
display: none;
}
</style>
<!-- Main content -->
<section class="content" style="zoom: 90%;">
<div class="container-fluid">
<div class="row">
<div class="col-12">
@@ -16,56 +23,66 @@
<div class="card-body">
<div class="row">
<div class="col-md-4">
<form method="post" enctype="multipart/form-data">
<div class="form-group">
<label>Data Server</label>
<div class="select2-primary">
<select class="select2" name="ids[]" multiple="multiple" data-placeholder="Select a State" data-dropdown-css-class="select2-primary" style="width: 100%;" required>
<div class="card-body">
<form method="post" enctype="multipart/form-data">
<div class="form-group">
<label>Tipe WhatsApp Gateway</label>
<select class="form-control" name="gateway_type" id="gateway_type" required>
<option value="unofficial">Unofficial WA API</option>
<option value="official">Official WA API</option>
</select>
</div>
<div class="form-group">
<label>Data Server</label>
<div class="select2-primary">
<select class="select2" name="ids[]" multiple="multiple" data-placeholder="Select a State" data-dropdown-css-class="select2-primary" style="width: 100%;" required>
<?php
$List = new Listdata();
$List->set_list("data_server",$SqlDataWH);
foreach ($List->get_list() as $key => $value): ?>
<option value="<?php echo $value['id']; ?>"><?php echo $value['nama_server']; ?></option>
<?php endforeach ?>
</select>
</div>
</div>
<div class="form-group">
<label>Status Pelanggan</label>
<select id="sp" class="form-control" name="sp">
<option value="">All</option>
<option value="aktif">Aktif</option>
<option value="tidak aktif" >Tidak Aktif</option>
</select>
</div>
<div class="form-group">
<label>Status Koneksi</label>
<select id="sk" class="form-control" name="sk">
<option value="">All</option>
<option value="On">Connected</option>
<option value="Off" >Disconnected</option>
</select>
</div>
<div class="form-group">
<label>Topologi ODP / ODC</label>
<select class="form-control select2bs4" name="mt">
<option value="">All</option>
<?php
$List = new Listdata();
$List->set_list("data_server",$SqlDataWH);
$List->set_list("master_topologi", $SqlDataWHId);
foreach ($List->get_list() as $key => $value): ?>
<option value="<?php echo $value['id']; ?>"><?php echo $value['nama_server']; ?></option>
<option value="<?php echo $value['id']; ?>" ><?php echo $value['nama']; ?></option>
<?php endforeach ?>
</select>
</div>
</div>
<div class="form-group">
<label>Status Pelanggan</label>
<select id="sp" class="form-control" name="sp">
<option value="">All</option>
<option value="aktif">Aktif</option>
<option value="tidak aktif" >Tidak Aktif</option>
</select>
</div>
<div class="form-group">
<label>Status Koneksi</label>
<select id="sk" class="form-control" name="sk">
<option value="">All</option>
<option value="On">Connected</option>
<option value="Off" >Disconnected</option>
</select>
</div>
<div class="form-group">
<label>Topologi ODP / ODC</label>
<select class="form-control select2bs4" name="mt">
<option value="">All</option>
<?php
$List = new Listdata();
$List->set_list("master_topologi", $SqlDataWHId);
foreach ($List->get_list() as $key => $value): ?>
<option value="<?php echo $value['id']; ?>" ><?php echo $value['nama']; ?></option>
<?php endforeach ?>
</select>
</div>
</div>
<div class="col-md-4">
<div class="col-md-4 unofficial-section">
<div class="form-group">
<label>Isi Pesan Siaran</label>
<textarea class="form-control" rows="11" name="pesan" required="required"></textarea>
<textarea class="form-control" rows="11" name="pesan"></textarea>
</div>
</div>
<div class="col-md-4">
<div class="col-md-4 unofficial-section">
<center>
<label>Foto</label>
<div class="uploadImage" data-id="siaran<?php echo $SvId; ?>">
@@ -77,6 +94,45 @@
</div>
</center>
</div>
<div class="col-md-3 official-section">
<div class="card-body">
<div class="form-group">
<label for="template">Pilih Template</label>
<select class="form-control" name="template" id="template">
<?php foreach ($templates as $tpl): ?>
<option
value="<?= htmlspecialchars($tpl['name']) ?>"
data-template='<?= json_encode($tpl, JSON_HEX_APOS | JSON_HEX_QUOT) ?>' <?= (htmlspecialchars($tpl['name']) == $pesan['nama_template']) ? 'selected' : '' ?>>
<?= htmlspecialchars($tpl['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<input type="hidden" name="pesan_official" id="pesan_official">
<!-- Form dinamis -->
<div id="paramForm" style="margin-top:20px;"></div>
</div>
</div>
<div class="col-md-5 official-section">
<div class="card-body" style="display:flex;justify-content:center;align-items:center;">
<!-- Preview gaya WhatsApp -->
<div id="waPreview" style="margin-top:20px;max-width:400px;font-family:sans-serif;">
<div style="background:#075E54;color:white;padding:10px;border-radius:8px 8px 0 0;">
WhatsApp Preview
</div>
<div style="border:1px solid #ccc;border-top:0;padding-top:10px;padding-left:10px;padding-right:10px;border-radius:0 0 8px 8px;background:#ECE5DD; padding-bottom: 20px;">
<div style="background:white;padding:8px;border-radius:8px;display:flex;flex-direction:column;gap:0;max-width:90%;margin-bottom:-6px;">
<div id="waHeader"></div>
<div id="waMessage"></div>
<div id="waFooter"></div>
</div>
<div id="waButtons" style="max-width:90%;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer">
@@ -90,12 +146,20 @@
</section>
<?php
if (isset($_POST['kirim'])) {
$idsv = implode(", ", $_POST['ids']);
$sp = $_POST['sp'];
$sk = $_POST['sk'];
$mt = $_POST['mt'];
$type = $_POST['gateway_type'] ?? null;
$ids = $_POST['ids'] ?? [];
// Cegah implode null
if (empty($ids)) {
sweetAlert("Tidak ada server yang dipilih!", "warning");
exit;
}
$idsv = implode(", ", array_map('intval', $ids)); // sanitasi ID
$sp = $_POST['sp'] ?? null;
$sk = $_POST['sk'] ?? null;
$mt = $_POST['mt'] ?? null;
$sp1 = !empty($sp) ? " AND status = :sp" : "";
$sk1 = !empty($sk) ? " AND status_koneksi = :sk" : "";
@@ -106,42 +170,78 @@ if (isset($_POST['kirim'])) {
try {
$stmt = $pdo->prepare("SELECT nomor_whatsapp, id_data_server FROM pelanggan $SQL");
if (!empty($sp)) $stmt->bindParam(':sp', $sp);
if (!empty($sk)) $stmt->bindParam(':sk', $sk);
if (!empty($mt)) $stmt->bindParam(':mt', $mt);
$stmt->execute();
$countInserted = 0;
while ($pecah = $stmt->fetch(PDO::FETCH_ASSOC)) {
$stmt2 = $pdo->prepare("SELECT url_whatsapp_gateway, apiKey_whatsapp FROM data_server WHERE id = :id_data_server");
$stmt2->bindParam(':id_data_server', $pecah['id_data_server']);
$stmt2->execute();
$pecah2 = $stmt2->fetch(PDO::FETCH_ASSOC);
if (isset($_SESSION['uploaded_files']['siaran'.$SvId])) {
$id = $pecah['id_data_server'];
$jenis = "sendMediaFromUrl";
$nomor = $pecah['nomor_whatsapp'];
$message = $_POST['pesan'];
$file_url = $target_dir . $_SESSION['uploaded_files']['siaran'.$SvId];
$apiurl = $pecah2['url_whatsapp_gateway'];
$apikey = $pecah2['apiKey_whatsapp'];
$sqlIn = "INSERT INTO master_pesan (id_data_server, jenis_pesan, api_url, api_key, nomor, pesan, url_file, status)
VALUES (:id, :jenis, :apiurl, :apikey, :nomor, :pesan, :url_file, 'PENDING')";
} else {
// ====== GATEWAY OFFICIAL ======
if (!empty($type) && $type == "official") {
$id = $pecah['id_data_server'];
$jenis = "sendMessage";
$nomor = $pecah['nomor_whatsapp'];
$message = $_POST['pesan'];
$apiurl = $pecah2['url_whatsapp_gateway'];
$apikey = $pecah2['apiKey_whatsapp'];
$sqlIn = "INSERT INTO master_pesan (id_data_server, jenis_pesan, api_url, api_key, nomor, pesan, status)
VALUES (:id, :jenis, :apiurl, :apikey, :nomor, :pesan, 'PENDING')";
// Replace placeholder #no_whatsapp dengan angka murni dari nomor
$cleanNumber = preg_replace('/\D/', '', $nomor);
$message = str_replace('#no_whatsapp', $cleanNumber, $_POST['pesan_official']);
$file_url = NULL;
$apiurl = 0;
$apikey = 0;
$sqlIn = "INSERT INTO master_pesan
(id_data_server, jenis_pesan, api_url, api_key, nomor, pesan, url_file, status, gateway_type)
VALUES
(:id, :jenis, :apiurl, :apikey, :nomor, :pesan, :url_file, 'PENDING', 'official')";
}
// ====== GATEWAY UNOFFICIAL ======
else {
$stmt2 = $pdo->prepare("SELECT url_whatsapp_gateway, apiKey_whatsapp FROM data_server WHERE id = :id_data_server");
$stmt2->bindParam(':id_data_server', $pecah['id_data_server']);
$stmt2->execute();
$pecah2 = $stmt2->fetch(PDO::FETCH_ASSOC);
if (!$pecah2) {
continue; // skip kalau data_server tidak ditemukan
}
// Cek apakah ada file yang diupload di sesi
$sessionKey = 'siaran' . $SvId;
if (isset($_SESSION['uploaded_files'][$sessionKey])) {
$id = $pecah['id_data_server'];
$jenis = "sendMediaFromUrl";
$nomor = $pecah['nomor_whatsapp'];
$message = $_POST['pesan'];
$file_url = $target_dir . $_SESSION['uploaded_files'][$sessionKey];
$apiurl = $pecah2['url_whatsapp_gateway'];
$apikey = $pecah2['apiKey_whatsapp'];
$sqlIn = "INSERT INTO master_pesan
(id_data_server, jenis_pesan, api_url, api_key, nomor, pesan, url_file, status, gateway_type)
VALUES
(:id, :jenis, :apiurl, :apikey, :nomor, :pesan, :url_file, 'PENDING', 'unofficial')";
} else {
$id = $pecah['id_data_server'];
$jenis = "sendMessage";
$nomor = $pecah['nomor_whatsapp'];
$message = $_POST['pesan'];
$apiurl = $pecah2['url_whatsapp_gateway'];
$apikey = $pecah2['apiKey_whatsapp'];
$file_url = NULL;
$sqlIn = "INSERT INTO master_pesan
(id_data_server, jenis_pesan, api_url, api_key, nomor, pesan, status, gateway_type)
VALUES
(:id, :jenis, :apiurl, :apikey, :nomor, :pesan, 'PENDING', 'unofficial')";
}
}
// === Insert ke tabel master_pesan ===
$stmtInsert = $pdo->prepare($sqlIn);
$stmtInsert->bindParam(':id', $id);
$stmtInsert->bindParam(':jenis', $jenis);
@@ -149,23 +249,347 @@ if (isset($_POST['kirim'])) {
$stmtInsert->bindParam(':apikey', $apikey);
$stmtInsert->bindParam(':nomor', $nomor);
$stmtInsert->bindParam(':pesan', $message);
if (isset($file_url)) {
if (strpos($sqlIn, ':url_file') !== false) {
$stmtInsert->bindParam(':url_file', $file_url);
}
$stmtInsert->execute();
$countInserted++;
}
sweetAlert("Pesan Akan Segera di Kirim", "success");
unset($_SESSION['uploaded_files']['siaran'.$SvId]);
// Bersihkan sesi upload jika ada
if (isset($_SESSION['uploaded_files']['siaran' . $SvId])) {
unset($_SESSION['uploaded_files']['siaran' . $SvId]);
}
if ($countInserted > 0) {
sweetAlert("Pesan ($countInserted) akan segera dikirim.", "success");
} else {
sweetAlert("Tidak ada data pelanggan yang cocok.", "info");
}
} catch (PDOException $e) {
// Handle exception, e.g., log the error
sweetAlert("Error: " . $e->getMessage(), "success");
// echo "Error: " . $e->getMessage();
sweetAlert("Error Database: " . $e->getMessage(), "error");
} catch (Exception $e) {
sweetAlert("Kesalahan Umum: " . $e->getMessage(), "error");
}
}
?>
<script>
document.addEventListener("DOMContentLoaded", function () {
const gatewaySelect = document.getElementById("gateway_type");
const unofficialSections = document.querySelectorAll(".unofficial-section");
const officialSections = document.querySelectorAll(".official-section");
const templateSelect = document.getElementById("template");
const paramForm = document.getElementById("paramForm");
const waHeader = document.getElementById("waHeader");
const waMessage = document.getElementById("waMessage");
const waFooter = document.getElementById("waFooter");
const waButtons = document.getElementById("waButtons");
const waPostmessage = document.getElementById("pesan_official");
const SvId = '<?= encrypt_url(json_encode([$SvId, "informasi"])); ?>';
let templateLoaded = false;
// ============================================================
// DEFINISI SEMUA FUNCTION UTAMA DI AWAL (biar pasti dikenal)
// ============================================================
function updatePreview() {
let preview = waMessage.getAttribute("data-original") || "";
document.querySelectorAll("#paramForm input[data-placeholder]").forEach(input => {
let ph = input.dataset.placeholder;
let val = input.value || ph;
preview = preview.replace(ph, val);
});
preview = preview
.replace(/\*(.*?)\*/g, "<b>$1</b>")
.replace(/__(.*?)__/g, "<u>$1</u>")
.replace(/_(.*?)_/g, "<i>$1</i>")
.replace(/~(.*?)~/g, "<s>$1</s>")
.replace(/\n/g, "<br>");
waMessage.innerHTML = preview;
const payload = generatePayload();
if (waPostmessage && payload) {
waPostmessage.value = JSON.stringify(payload, null, 2);
}
}
function updateHeaderImage(input) {
const img = document.getElementById("header-img-preview");
if (img) img.src = input.value || "";
updatePreview();
}
function generatePayload() {
const template = templateSelect.value;
if (!template) return null;
const data = {
messaging_product: "whatsapp",
recipient_type: "individual",
to: "#no_whatsapp",
type: "template",
template: {
name: template,
language: { code: "id" },
components: []
}
};
const headerImageInput = document.querySelector("input[name='header_image']");
if (headerImageInput?.value) {
data.template.components.push({
type: "header",
parameters: [{ type: "image", image: { link: headerImageInput.value } }]
});
}
const bodyParams = [];
document.querySelectorAll("input[name^='body_']").forEach(input => {
if (input.value.trim()) {
bodyParams.push({ type: "text", text: input.value });
}
});
if (bodyParams.length > 0) {
data.template.components.push({ type: "body", parameters: bodyParams });
}
document.querySelectorAll("input[name^='btn_']").forEach(input => {
if (input.value.trim()) {
let [_, index] = input.name.split("_");
data.template.components.push({
type: "button",
sub_type: "url",
index: parseInt(index),
parameters: [{ type: "text", text: input.value }]
});
}
});
return data;
}
function initImageUploader() {
const imgInput = document.getElementById("image_update_value");
const imgPreview = document.getElementById("header-img-preview");
const container = document.getElementById("upload_container");
if (!imgInput || !container) return;
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = "image/*";
fileInput.className = "form-control mt-2";
fileInput.addEventListener("change", async (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = ev => { if (imgPreview) imgPreview.src = ev.target.result; };
reader.readAsDataURL(file);
const formData = new FormData();
formData.append("file", file);
try {
const res = await fetch("controller/upload_image_pesan_siaran.php", {
method: "POST",
body: formData
});
const data = await res.json();
if (data.status === "success" && data.url) {
imgInput.value = data.url;
updateHeaderImage(imgInput);
} else {
alert("Upload gagal: " + (data.message || "Unknown error"));
}
} catch (err) {
alert("Upload gagal. Cek koneksi atau server upload.");
console.error(err);
}
});
container.innerHTML = "";
container.appendChild(fileInput);
}
// ============================================================
// RENDER PARAMETER DINAMIS (dengan binding event via JS)
// ============================================================
function renderParamForm(tpl) {
paramForm.innerHTML = "";
waHeader.innerHTML = "";
waMessage.innerHTML = "";
waFooter.innerHTML = "";
waButtons.innerHTML = "";
if (!tpl?.components) return;
let formHtml = "<h5 class='text-center'><b>Parameter Wajib</b></h5><hr>";
let previewBody = "";
tpl.components.forEach((comp) => {
// HEADER IMAGE
if (comp.type === "HEADER" && comp.format === "IMAGE") {
let defaultImg = comp.example?.header_handle?.[0] || "";
formHtml += `
<div class="form-group">
<label>Gambar</label>
<input class="form-control" type="hidden" id="image_update_value"
name="header_image" value="${defaultImg}" required>
<div id="upload_container"></div>
</div>`;
waHeader.innerHTML = `<img id="header-img-preview" src="${defaultImg}" style="max-width:100%;border-radius:6px;"><br>`;
}
// BODY PARAMS
if (comp.type === "BODY" && comp.text) {
waMessage.setAttribute("data-original", comp.text);
let regex = /\{\{(\d+)\}\}/g, match, idx = 0;
while ((match = regex.exec(comp.text)) !== null) {
let paramNum = match[1];
formHtml += `
<div class="form-group">
<label>Parameter : {{${paramNum}}}</label>
<input class="form-control body-param" type="text"
name="body_${paramNum}" data-placeholder="{{${paramNum}}}" required>
</div>`;
idx++;
}
previewBody += comp.text.replace(/\n/g, "<br>");
}
if (comp.type === "FOOTER" && comp.text) {
waFooter.innerHTML = `<small class="text-secondary">${comp.text}</small><br>`;
}
if (comp.type === "BUTTONS" && comp.buttons) {
comp.buttons.forEach((btn, bi) => {
if (btn.type === "URL") {
let btnText = btn.text;
let url = btn.url;
let regex = /\{\{(\d+)\}\}/g;
let match, idx = 0;
while ((match = regex.exec(url)) !== null) {
let paramNum = match[1];
let dbVal = btn.dbParams ? (btn.dbParams[idx] || "") : "";
formHtml += `<div class="form-group">
<label>Button ${bi+1} Param ${paramNum}:</label><br>
<input class="form-control" type="text" name="btn_${bi}_${paramNum}"
value="${dbVal}"
oninput="updatePreview()" data-placeholder="{{${paramNum}}}" required>
</div>`;
idx++;
}
waButtons.innerHTML += `<a href="#"
style="display:flex;align-items:center;justify-content:center;
background:#fff;color:green;padding:8px 12px;
border-radius:6px;border-top:1px solid #c0c0c0;
text-decoration:none;position:relative;z-index:10;">
<i class="fa fa-share-square" style="margin-right:8px; font-size:16px;"></i>
<span>${btnText}</span>
</a>`;
}
});
}
});
paramForm.innerHTML = formHtml;
waMessage.innerHTML = previewBody;
// Tambahkan listener dinamis untuk semua input yang baru dibuat
paramForm.querySelectorAll("input[data-placeholder]").forEach(input => {
input.addEventListener("input", updatePreview);
});
const imgInput = document.getElementById("image_update_value");
if (imgInput) imgInput.addEventListener("input", e => updateHeaderImage(e.target));
updatePreview();
initImageUploader();
}
// ============================================================
// FETCH TEMPLATE OFFICIAL
// ============================================================
function loadOfficialTemplates() {
fetch(`controller/get_template_official.php?id=${SvId}`)
.then(res => res.json())
.then(data => {
if (data.error) {
templateSelect.innerHTML = `<option disabled selected>${data.error}</option>`;
return;
}
const templates = Array.isArray(data.templates) ? data.templates : [];
const filtered = templates.filter(t => t.name?.startsWith("informasi_"));
if (filtered.length === 0) {
templateSelect.innerHTML = `<option disabled selected>Tidak ada template informasi_</option>`;
return;
}
templateSelect.innerHTML = "";
filtered.forEach(tpl => {
const opt = document.createElement("option");
opt.value = tpl.name;
opt.textContent = tpl.name;
opt.dataset.template = JSON.stringify(tpl);
templateSelect.appendChild(opt);
});
if (templateSelect.options.length > 0) {
templateSelect.selectedIndex = 0;
const firstTpl = JSON.parse(templateSelect.options[0].dataset.template);
renderParamForm(firstTpl);
}
})
.catch(err => {
console.error("Gagal load template:", err);
templateSelect.innerHTML = `<option disabled selected>Gagal load template</option>`;
});
}
// ============================================================
// TOGGLE GATEWAY VIEW
// ============================================================
function toggleGatewayView() {
const val = gatewaySelect.value;
if (val === "unofficial") {
unofficialSections.forEach(el => el.style.display = "block");
officialSections.forEach(el => el.style.display = "none");
} else if (val === "official") {
unofficialSections.forEach(el => el.style.display = "none");
officialSections.forEach(el => el.style.display = "block");
if (!templateLoaded) {
loadOfficialTemplates();
templateLoaded = true;
}
}
}
// ============================================================
// EVENT LISTENERS
// ============================================================
templateSelect.addEventListener("change", function () {
const opt = this.options[this.selectedIndex];
if (!opt?.dataset.template) return;
const tpl = JSON.parse(opt.dataset.template);
renderParamForm(tpl);
});
gatewaySelect.addEventListener("change", toggleGatewayView);
toggleGatewayView();
});
</script>