i18n: terjemahkan sisa fragmen hint (billing/map/cash_in/acs/areas/kolektor/pelanggan/radius/diskon/wlc/preview/broadcast/packages/reports/whatsapp/settings)

This commit is contained in:
oxpoe committed 2026-10-09 10:49:44 +07:00
1 parent fd6f1ebd93
commit aca2ea06e3
16 files changed
+155 -63

No files matched your search

+62 -16
View File
@@ -932,7 +932,8 @@
"empty_cust": "No customer data yet.", "empty_cust": "No customer data yet.",
"empty_odp": "No ODP data yet.", "empty_odp": "No ODP data yet.",
"view_port": "Click to view port visual", "view_port": "Click to view port visual",
"port_err": "Failed to load port data." "port_err": "Failed to load port data.",
"route_hint": "Drag the orange points to change the route. Click the button below to add a bend point."
}, },
"print_invoice": { "print_invoice": {
"title": "Print Invoice Receipt", "title": "Print Invoice Receipt",
@@ -1116,7 +1117,8 @@
"run_prov": "Run Provisioning", "run_prov": "Run Provisioning",
"add_server": "Add ACS Server", "add_server": "Add ACS Server",
"bind_lan": "Select the LAN Ports to bind:", "bind_lan": "Select the LAN Ports to bind:",
"bind_ssid": "Select the WLAN (SSID) to bind:" "bind_ssid": "Select the WLAN (SSID) to bind:",
"load_log": " to load logs."
}, },
"vd": { "vd": {
"action": "Action", "action": "Action",
@@ -1146,7 +1148,11 @@
"tab_templates": "Message Templates", "tab_templates": "Message Templates",
"tab_livechat": "Live Chat", "tab_livechat": "Live Chat",
"tab_preview": "Message Preview", "tab_preview": "Message Preview",
"ph_deviceid": "Device ID (optional)" "ph_deviceid": "Device ID (optional)",
"selfhost": " or Self-Hosted Server URL)",
"fonnte_hint": " for Fonnte Cloud, or switch to your own server domain/IP when using ",
"qr_hint": "Click the button, then open WhatsApp → Linked Devices → Link a device, scan the QR. The QR is valid for ~30 seconds & refreshes automatically.",
"notif_only": " replies to commands anymore — WhatsApp is only for "
}, },
"automation": { "automation": {
"title": "Automation", "title": "Automation",
@@ -1641,7 +1647,8 @@
"approve_invoice": "Approve & Create Invoice", "approve_invoice": "Approve & Create Invoice",
"modem_hint": "Customer modem login credentials for RADIUS & PPPoE.", "modem_hint": "Customer modem login credentials for RADIUS & PPPoE.",
"reg_hint": "Automatically calculated on install/payment. Can be adjusted manually if needed.", "reg_hint": "Automatically calculated on install/payment. Can be adjusted manually if needed.",
"modem_hint_mt": "Customer modem login credentials for PPPoE MikroTik API." "modem_hint_mt": "Customer modem login credentials for PPPoE MikroTik API.",
"reg_auto": " automatically: arrears "
}, },
"bill": { "bill": {
"ph_note": "e.g. service credit for downtime", "ph_note": "e.g. service credit for downtime",
@@ -1668,7 +1675,8 @@
"all_routers": "All Routers", "all_routers": "All Routers",
"all_collectors": "All Collectors", "all_collectors": "All Collectors",
"opt_percent": "Percent (%)", "opt_percent": "Percent (%)",
"opt_amount": "Amount (Rp)" "opt_amount": "Amount (Rp)",
"pct_hint": " The percentage is calculated from the base price (before VAT/USO). The QRIS unique code will be regenerated."
}, },
"common": { "common": {
"keep_empty": "Leave empty to keep unchanged", "keep_empty": "Leave empty to keep unchanged",
@@ -1820,7 +1828,8 @@
"alert_copied": "MikroTik & RADIUS Isolir setup script copied! Paste it into your MikroTik Terminal.", "alert_copied": "MikroTik & RADIUS Isolir setup script copied! Paste it into your MikroTik Terminal.",
"alert_copied2": "MikroTik script copied!", "alert_copied2": "MikroTik script copied!",
"deactivate": "Deactivate", "deactivate": "Deactivate",
"activate": "Activate" "activate": "Activate",
"rate_from_mt": "rate-limit & IP are taken from the PPP profile on MikroTik"
}, },
"set": { "set": {
"ew_log_realtime": "Realtime Log", "ew_log_realtime": "Realtime Log",
@@ -1921,7 +1930,21 @@
"office_coord_hint": "Office coordinates are used for employee attendance location validation (geofencing).", "office_coord_hint": "Office coordinates are used for employee attendance location validation (geofencing).",
"set_auto_moved": "Automation moved:", "set_auto_moved": "Automation moved:",
"set_auto_list": "Auto-Deactivate, Auto-Delete, Auto-Warning & Auto-", "set_auto_list": "Auto-Deactivate, Auto-Delete, Auto-Warning & Auto-",
"set_auto_now": "are now organized in the" "set_auto_now": "are now organized in the",
"link_hint": "Used to build the customer login link sent via WhatsApp. Example: ",
"wa_numbers_hint": "Admin numbers receive system monitoring notifications. Technician numbers are taken automatically from ",
"logo_hint": "PNG/JPG/WebP, max 2MB. PNG/JPEG is auto-optimized. Used on the login page & customer portal.",
"pwa_icon_hint": "After upload, PWA icons (192/512/maskable/apple-touch) & favicon are regenerated from this image. A square image (1:1) is recommended.",
"hero_hint": "PNG/JPEG is automatically converted to ",
"speedtest_hint2": " (LibreSpeed / OpenSpeedTest self-hosted) or ",
"speedtest_hint3": ". If left empty, the customer dashboard shows ",
"speedtest_notset": "\"Speedtest not set up yet\"",
"speedtest_btn_hint": "On the customer page there are buttons ",
"repo_hint": "Used for the link ",
"uptime_hint": "Used for the button/redirect ",
"qris_unique_hint": " in the Billing menu to create a unique amount per invoice.",
"wh_hint": " = the URL above (secret already included), ",
"wh_amount_hint": "equal to the invoice unique amount"
}, },
"mt": { "mt": {
"updating": "Updating...", "updating": "Updating...",
@@ -1954,7 +1977,8 @@
"selected": "selected", "selected": "selected",
"history": "Discount History (last 100)", "history": "Discount History (last 100)",
"th_note": "Note", "th_note": "Note",
"empty_cust": "No customers." "empty_cust": "No customers.",
"unpaid": "unpaid"
}, },
"cpay": { "cpay": {
"title": "Collector Payment Approval", "title": "Collector Payment Approval",
@@ -2050,10 +2074,17 @@
"prorata_first": "First invoice proration (install date = billing month)", "prorata_first": "First invoice proration (install date = billing month)",
"night_hint": "The system switches customers to this PPP profile between 00:00 - 06:00.", "night_hint": "The system switches customers to this PPP profile between 00:00 - 06:00.",
"kalong_hint": "Check to configure (fields open automatically if this package uses Jam Kalong).", "kalong_hint": "Check to configure (fields open automatically if this package uses Jam Kalong).",
"fup_hint": "Check to configure (fields open automatically if this package uses FUP).", "fup_hint": "). The same value is used for the MikroTik profile ",
"active_days_hint": "The customer stays active for X days since activation / payment.", "active_days_hint": "The customer stays active for X days since activation / payment.",
"ratelimit_hint": "Actual rate-limit (MikroTik & RADIUS):", "ratelimit_hint": "Actual rate-limit (MikroTik & RADIUS):",
"profile_hint2": "PPP profile + IP pool are created/updated automatically on MikroTik (MikroTik / RADIUS-profile mode). In RADIUS-attribute mode, IP & rate-limit come from RADIUS." "profile_hint2": "PPP profile + IP pool are created/updated automatically on MikroTik (MikroTik / RADIUS-profile mode). In RADIUS-attribute mode, IP & rate-limit come from RADIUS.",
"radius_hint3": " — the values above are read from RADIUS settings. To change, open ",
"mt_hint3": " — the values above are read from the PPP profile on the router. To change, edit in ",
"profile_a": " The MikroTik PPP profile will be ",
"profile_b": "created/updated automatically",
"profile_c": " (RADIUS mode uses MikroTik profile)",
"prorata_a": "Invoice proration ",
"prorata_b": " if installed mid-month (proportion of remaining days; requires install date in customer data)"
}, },
"col": { "col": {
"title": "Collector Management", "title": "Collector Management",
@@ -2071,7 +2102,8 @@
"full_name": "Full Name", "full_name": "Full Name",
"area_region": "Area / Assignment Region", "area_region": "Area / Assignment Region",
"opt_all_area": "-- All Areas / Unrestricted --", "opt_all_area": "-- All Areas / Unrestricted --",
"empty": "No collector data yet." "empty": "No collector data yet.",
"mode_hint": "` means the collector payment directly settles the invoice, while `"
}, },
"mon": { "mon": {
"title": "System Monitoring", "title": "System Monitoring",
@@ -2202,7 +2234,8 @@
"save": "Save Income", "save": "Save Income",
"title": "Cash In", "title": "Cash In",
"th_method": "Method", "th_method": "Method",
"empty": "No cash-in data yet." "empty": "No cash-in data yet.",
"info": " This page is used to record cash-in / income "
}, },
"exp": { "exp": {
"heading": "Expenses (Cash Out)", "heading": "Expenses (Cash Out)",
@@ -2244,7 +2277,8 @@
"save": "Save Area", "save": "Save Area",
"edit": "Edit Area Data", "edit": "Edit Area Data",
"title": "Area Management", "title": "Area Management",
"th_name": "Area / Region Name" "th_name": "Area / Region Name",
"create_area": " to create an area."
}, },
"rt": { "rt": {
"list": "MikroTik Router List", "list": "MikroTik Router List",
@@ -2275,7 +2309,10 @@
"delay_sec": "Delay (seconds)", "delay_sec": "Delay (seconds)",
"stop": "Stop", "stop": "Stop",
"moved": "Auto reminder moved:", "moved": "Auto reminder moved:",
"moved_desc": "schedule, template selection & reminder message settings are now in the" "moved_desc": "schedule, template selection & reminder message settings are now in the",
"send_to": " Send to customers ",
"unpaid": "unpaid",
"on_date": " on the selected isolir date. Use "
}, },
"bk": { "bk": {
"title": "Backup & Recovery", "title": "Backup & Recovery",
@@ -2333,10 +2370,19 @@
}, },
"rep": { "rep": {
"ppn_incl": "Yes — VAT already included", "ppn_incl": "Yes — VAT already included",
"tax_hint": "Corporate income tax, VAT, USO & BHP are calculated automatically from this value." "tax_hint": "Corporate income tax, VAT, USO & BHP are calculated automatically from this value.",
"export_hint": "— export & print monthly",
"sum_hint": " = sum of each month; match with ",
"audit_hint": " (year) above for auditing.",
"calc_hint": "— calculated automatically"
}, },
"wlc": { "wlc": {
"empty": "No messages with this customer yet. Type a message below and press Enter to start." "empty": "No messages with this customer yet. Type a message below and press Enter to start.",
"loading": " Loading messages..."
},
"wp": {
"auto": "automatic message",
"sample": " with sample data — "
} }
}, },
"tech": { "tech": {
+62 -16
View File
@@ -932,7 +932,8 @@
"empty_cust": "Belum ada data pelanggan.", "empty_cust": "Belum ada data pelanggan.",
"empty_odp": "Belum ada data ODP.", "empty_odp": "Belum ada data ODP.",
"view_port": "Klik untuk lihat visual port", "view_port": "Klik untuk lihat visual port",
"port_err": "Gagal memuat data port." "port_err": "Gagal memuat data port.",
"route_hint": "Tarik titik oranye untuk mengubah jalur. Klik tombol di bawah untuk menambah titik lekukan."
}, },
"print_invoice": { "print_invoice": {
"title": "Cetak Struk Tagihan", "title": "Cetak Struk Tagihan",
@@ -1116,7 +1117,8 @@
"run_prov": "Jalankan Provisioning", "run_prov": "Jalankan Provisioning",
"add_server": "Tambah Server ACS", "add_server": "Tambah Server ACS",
"bind_lan": "Pilih LAN Port yang akan di-bind:", "bind_lan": "Pilih LAN Port yang akan di-bind:",
"bind_ssid": "Pilih WLAN (SSID) yang akan di-bind:" "bind_ssid": "Pilih WLAN (SSID) yang akan di-bind:",
"load_log": " untuk memuat log."
}, },
"vd": { "vd": {
"action": "Aksi", "action": "Aksi",
@@ -1146,7 +1148,11 @@
"tab_templates": "Template Pesan", "tab_templates": "Template Pesan",
"tab_livechat": "Live Chat", "tab_livechat": "Live Chat",
"tab_preview": "Preview Pesan", "tab_preview": "Preview Pesan",
"ph_deviceid": "Device ID (opsional)" "ph_deviceid": "Device ID (opsional)",
"selfhost": " atau URL Server Self-Hosted)",
"fonnte_hint": " untuk Fonnte Cloud, atau ubah ke domain/IP server mandiri jika menggunakan ",
"qr_hint": "Klik tombol, lalu buka WhatsApp → Perangkat Tertaut → Tautkan perangkat, scan QR. QR berlaku ~30 detik & diperbarui otomatis.",
"notif_only": " membalas perintah lagi — WhatsApp hanya untuk "
}, },
"automation": { "automation": {
"title": "Otomatisasi", "title": "Otomatisasi",
@@ -1642,7 +1648,8 @@
"approve_invoice": "Setujui & Buat Tagihan", "approve_invoice": "Setujui & Buat Tagihan",
"modem_hint": "Kredensial login modem pelanggan untuk RADIUS & PPPoE.", "modem_hint": "Kredensial login modem pelanggan untuk RADIUS & PPPoE.",
"reg_hint": "Otomatis dihitung saat pasang/bayar. Bisa disesuaikan manual jika perlu.", "reg_hint": "Otomatis dihitung saat pasang/bayar. Bisa disesuaikan manual jika perlu.",
"modem_hint_mt": "Kredensial login modem pelanggan untuk PPPoE MikroTik API." "modem_hint_mt": "Kredensial login modem pelanggan untuk PPPoE MikroTik API.",
"reg_auto": " otomatis: menunggak "
}, },
"bill": { "bill": {
"ph_note": "mis. kompensasi gangguan", "ph_note": "mis. kompensasi gangguan",
@@ -1669,7 +1676,8 @@
"all_routers": "Semua Router", "all_routers": "Semua Router",
"all_collectors": "Semua Kolektor", "all_collectors": "Semua Kolektor",
"opt_percent": "Persen (%)", "opt_percent": "Persen (%)",
"opt_amount": "Nominal (Rp)" "opt_amount": "Nominal (Rp)",
"pct_hint": " Persen dihitung dari harga dasar (sebelum PPN/USO). Kode unik QRIS akan digenerate ulang."
}, },
"common": { "common": {
"keep_empty": "Biarkan kosong jika tidak diganti", "keep_empty": "Biarkan kosong jika tidak diganti",
@@ -1821,7 +1829,8 @@
"alert_copied": "Script setup MikroTik & Isolir RADIUS berhasil disalin! Silakan tempelkan (paste) di Terminal MikroTik Anda.", "alert_copied": "Script setup MikroTik & Isolir RADIUS berhasil disalin! Silakan tempelkan (paste) di Terminal MikroTik Anda.",
"alert_copied2": "Script MikroTik berhasil disalin!", "alert_copied2": "Script MikroTik berhasil disalin!",
"deactivate": "Non-aktifkan", "deactivate": "Non-aktifkan",
"activate": "Aktifkan" "activate": "Aktifkan",
"rate_from_mt": "rate-limit & IP diambil dari profil PPP di MikroTik"
}, },
"set": { "set": {
"ew_log_realtime": "Log Realtime", "ew_log_realtime": "Log Realtime",
@@ -1922,7 +1931,21 @@
"office_coord_hint": "Koordinat kantor digunakan untuk validasi lokasi absensi karyawan (geofencing)", "office_coord_hint": "Koordinat kantor digunakan untuk validasi lokasi absensi karyawan (geofencing)",
"set_auto_moved": "Otomatisasi dipindah:", "set_auto_moved": "Otomatisasi dipindah:",
"set_auto_list": "Auto-Nonaktif, Auto-Hapus, Auto-Peringatan & Auto-", "set_auto_list": "Auto-Nonaktif, Auto-Hapus, Auto-Peringatan & Auto-",
"set_auto_now": "kini tertata di menu" "set_auto_now": "kini tertata di menu",
"link_hint": "Digunakan untuk menyusun tautan login pelanggan yang dikirim via WhatsApp. Contoh: ",
"wa_numbers_hint": "Nomor admin akan menerima notifikasi monitoring sistem. Nomor teknisi diambil otomatis dari ",
"logo_hint": "PNG/JPG/WebP, maks 2MB. PNG/JPEG otomatis dioptimalkan. Dipakai di halaman login & portal pelanggan.",
"pwa_icon_hint": "Setelah upload, ikon PWA (192/512/maskable/apple-touch) & favicon otomatis dibuat ulang dari gambar ini. Sebaiknya gambar persegi (1:1).",
"hero_hint": "PNG/JPEG otomatis dikonversi ke ",
"speedtest_hint2": " (LibreSpeed / OpenSpeedTest self-hosted) atau ",
"speedtest_hint3": ". Jika dikosongkan, dashboard pelanggan menampilkan ",
"speedtest_notset": "\"Speedtest belum di setup\"",
"speedtest_btn_hint": "Di halaman pelanggan tersedia tombol ",
"repo_hint": "Dipakai untuk tautan ",
"uptime_hint": "Dipakai untuk tombol/redirect ",
"qris_unique_hint": " di menu Billing untuk membuat nominal unik per invoice.",
"wh_hint": " = URL di atas (secret sudah termasuk), ",
"wh_amount_hint": "sama dengan nominal unik tagihan"
}, },
"mt": { "mt": {
"updating": "Memperbarui...", "updating": "Memperbarui...",
@@ -1955,7 +1978,8 @@
"selected": "dipilih", "selected": "dipilih",
"history": "Riwayat Diskon (100 terakhir)", "history": "Riwayat Diskon (100 terakhir)",
"th_note": "Catatan", "th_note": "Catatan",
"empty_cust": "Tidak ada pelanggan." "empty_cust": "Tidak ada pelanggan.",
"unpaid": "belum bayar"
}, },
"cpay": { "cpay": {
"title": "Approval Pembayaran Kolektor", "title": "Approval Pembayaran Kolektor",
@@ -2051,10 +2075,17 @@
"prorata_first": "Prorata tagihan pertama (tanggal pasang = bulan tagihan)", "prorata_first": "Prorata tagihan pertama (tanggal pasang = bulan tagihan)",
"night_hint": "Sistem akan ganti profile ppp pelanggan ke profile ini pada jam 00:00 - 06:00.", "night_hint": "Sistem akan ganti profile ppp pelanggan ke profile ini pada jam 00:00 - 06:00.",
"kalong_hint": "Centang untuk mengatur (kolom terbuka otomatis bila paket ini memakai Jam Kalong).", "kalong_hint": "Centang untuk mengatur (kolom terbuka otomatis bila paket ini memakai Jam Kalong).",
"fup_hint": "Centang untuk mengatur (kolom terbuka otomatis bila paket ini memakai FUP).", "fup_hint": "). Nilai sama dipakai untuk profil MikroTik ",
"active_days_hint": "Pelanggan aktif selama X hari sejak aktivasi / pembayaran.", "active_days_hint": "Pelanggan aktif selama X hari sejak aktivasi / pembayaran.",
"ratelimit_hint": "Rate-limit asli (MikroTik & RADIUS):", "ratelimit_hint": "Rate-limit asli (MikroTik & RADIUS):",
"profile_hint2": "Profil PPP + IP pool dibuat/diperbarui otomatis ke MikroTik (mode MikroTik / RADIUS profil). Pada mode RADIUS-atribut, IP & rate-limit dari RADIUS." "profile_hint2": "Profil PPP + IP pool dibuat/diperbarui otomatis ke MikroTik (mode MikroTik / RADIUS profil). Pada mode RADIUS-atribut, IP & rate-limit dari RADIUS.",
"radius_hint3": " — nilai di atas dibaca dari pengaturan RADIUS. Untuk mengubah, buka ",
"mt_hint3": " — nilai di atas dibaca dari profil PPP di router. Untuk mengubah, edit di ",
"profile_a": " Profil PPP MikroTik akan ",
"profile_b": "dibuat/diperbarui otomatis",
"profile_c": " (mode RADIUS pakai profil MikroTik)",
"prorata_a": "Prorata tagihan ",
"prorata_b": " jika pasang di tengah bulan (proporsi sisa hari, perlu tanggal pasang di data pelanggan)"
}, },
"col": { "col": {
"title": "Manajemen Kolektor", "title": "Manajemen Kolektor",
@@ -2072,7 +2103,8 @@
"full_name": "Nama Lengkap", "full_name": "Nama Lengkap",
"area_region": "Area / Wilayah Tugas", "area_region": "Area / Wilayah Tugas",
"opt_all_area": "-- Semua Area / Bebas --", "opt_all_area": "-- Semua Area / Bebas --",
"empty": "Belum ada data kolektor." "empty": "Belum ada data kolektor.",
"mode_hint": "` berarti pembayaran dari kolektor langsung melunasi invoice, sedangkan `"
}, },
"mon": { "mon": {
"title": "Monitoring Sistem", "title": "Monitoring Sistem",
@@ -2203,7 +2235,8 @@
"save": "Simpan Pemasukan", "save": "Simpan Pemasukan",
"title": "Kas Masuk", "title": "Kas Masuk",
"th_method": "Metode", "th_method": "Metode",
"empty": "Belum ada data kas masuk." "empty": "Belum ada data kas masuk.",
"info": " Halaman ini digunakan untuk mencatat kas masuk / pemasukan "
}, },
"exp": { "exp": {
"heading": "Pengeluaran (Cash Out)", "heading": "Pengeluaran (Cash Out)",
@@ -2245,7 +2278,8 @@
"save": "Simpan Area", "save": "Simpan Area",
"edit": "Edit Data Area", "edit": "Edit Data Area",
"title": "Manajemen Area", "title": "Manajemen Area",
"th_name": "Nama Area / Wilayah" "th_name": "Nama Area / Wilayah",
"create_area": " untuk membuat area."
}, },
"rt": { "rt": {
"list": "Daftar Router MikroTik", "list": "Daftar Router MikroTik",
@@ -2276,7 +2310,10 @@
"delay_sec": "Jeda (detik)", "delay_sec": "Jeda (detik)",
"stop": "Hentikan", "stop": "Hentikan",
"moved": "Pengingat otomatis dipindah:", "moved": "Pengingat otomatis dipindah:",
"moved_desc": "pengaturan jadwal, pemilihan template & isi pesan pengingat tagihan kini di menu" "moved_desc": "pengaturan jadwal, pemilihan template & isi pesan pengingat tagihan kini di menu",
"send_to": " Kirim ke pelanggan ",
"unpaid": "belum bayar",
"on_date": " pada tanggal isolir terpilih. Gunakan "
}, },
"bk": { "bk": {
"title": "Backup & Recovery", "title": "Backup & Recovery",
@@ -2334,10 +2371,19 @@
}, },
"rep": { "rep": {
"ppn_incl": "Ya — PPN sudah termasuk", "ppn_incl": "Ya — PPN sudah termasuk",
"tax_hint": "PPh Badan, PPN, USO & BHP dihitung otomatis dari nilai ini." "tax_hint": "PPh Badan, PPN, USO & BHP dihitung otomatis dari nilai ini.",
"export_hint": "— export & cetak tiap bulan",
"sum_hint": " = penjumlahan tiap bulan; cocokkan dengan ",
"audit_hint": " (tahun) di atas untuk audit.",
"calc_hint": "— dihitung otomatis"
}, },
"wlc": { "wlc": {
"empty": "Belum ada pesan dengan pelanggan ini. Ketik pesan di bawah dan tekan Enter untuk memulai." "empty": "Belum ada pesan dengan pelanggan ini. Ketik pesan di bawah dan tekan Enter untuk memulai.",
"loading": " Memuat pesan..."
},
"wp": {
"auto": "pesan otomatis",
"sample": " dengan data contoh — "
} }
}, },
"tech": { "tech": {
+1 -1
View File
@@ -266,7 +266,7 @@
<button class="btn btn-p btn-sm" onclick="loadAcsLogs()"><i class="bi bi-arrow-clockwise"></i> <%= t('admin.acs.reload','Muat Ulang') %></button> <button class="btn btn-p btn-sm" onclick="loadAcsLogs()"><i class="bi bi-arrow-clockwise"></i> <%= t('admin.acs.reload','Muat Ulang') %></button>
<span style="font-size:12px; color:var(--muted);">Log diambil dari tiap ACS (Built-in: riwayat task; GenieACS: <code>/faults</code>).</span> <span style="font-size:12px; color:var(--muted);">Log diambil dari tiap ACS (Built-in: riwayat task; GenieACS: <code>/faults</code>).</span>
</div> </div>
<div id="acsLogContainer"><div class="text-muted" style="padding:12px;">Pilih filter lalu klik "<%= t('admin.acs.reload','Muat Ulang') %>" untuk memuat log.</div></div> <div id="acsLogContainer"><div class="text-muted" style="padding:12px;">Pilih filter lalu klik "<%= t('admin.acs.reload','Muat Ulang') %>"<%= t('admin.acs.load_log',' untuk memuat log.') %></div></div>
</div> </div>
</div> </div>
<footer style="margin-top: 30px; padding: 20px; text-align: center; color: var(--muted); font-size: 12px; border-top: 1px solid var(--border);"> <footer style="margin-top: 30px; padding: 20px; text-align: center; color: var(--muted); font-size: 12px; border-top: 1px solid var(--border);">
+1 -1
View File
@@ -49,7 +49,7 @@
</thead> </thead>
<tbody> <tbody>
<% if (!areas || areas.length === 0) { %> <% if (!areas || areas.length === 0) { %>
<tr><td colspan="6" class="text-center text-muted" style="padding:24px">Belum ada data area. Klik "<%= t('admin.ar.add','Tambah Area Baru') %>" untuk membuat area.</td></tr> <tr><td colspan="6" class="text-center text-muted" style="padding:24px">Belum ada data area. Klik "<%= t('admin.ar.add','Tambah Area Baru') %>"<%= t('admin.ar.create_area',' untuk membuat area.') %></td></tr>
<% } else { %> <% } else { %>
<% areas.forEach((a, idx) => { %> <% areas.forEach((a, idx) => { %>
<tr> <tr>
+1 -1
View File
@@ -303,7 +303,7 @@
</div> </div>
</div> </div>
<div class="fg"><label class="fl"><%= t('admin.bill.note_optional','Catatan (opsional)') %></label><input class="fc" name="note" placeholder="<%= t('admin.bill.ph_note','mis. kompensasi gangguan') %>"></div> <div class="fg"><label class="fl"><%= t('admin.bill.note_optional','Catatan (opsional)') %></label><input class="fc" name="note" placeholder="<%= t('admin.bill.ph_note','mis. kompensasi gangguan') %>"></div>
<div class="hint"><i class="bi bi-info-circle"></i> Persen dihitung dari harga dasar (sebelum PPN/USO). Kode unik QRIS akan digenerate ulang.</div> <div class="hint"><i class="bi bi-info-circle"></i><%= t('admin.bill.pct_hint',' Persen dihitung dari harga dasar (sebelum PPN/USO). Kode unik QRIS akan digenerate ulang.') %></div>
</div> </div>
<div class="mf"> <div class="mf">
<button type="button" class="btn btn-g" onclick="closeModal('discModal')">Batal</button> <button type="button" class="btn btn-g" onclick="closeModal('discModal')">Batal</button>
+2 -2
View File
@@ -115,7 +115,7 @@
<div class="card mb16"> <div class="card mb16">
<div class="card-hd"><h6><i class="bi bi-send-check"></i> <%= t('admin.bc.send_title','Kirim Tagihan Sekarang (Manual — bila Auto OFF)') %></h6></div> <div class="card-hd"><h6><i class="bi bi-send-check"></i> <%= t('admin.bc.send_title','Kirim Tagihan Sekarang (Manual — bila Auto OFF)') %></h6></div>
<form method="POST" action="/admin/whatsapp/send-billing-now" onsubmit="return confirm('Kirim tagihan sekarang ke pelanggan belum bayar?');"> <form method="POST" action="/admin/whatsapp/send-billing-now" onsubmit="return confirm('Kirim tagihan sekarang ke pelanggan <%= t('admin.bc.unpaid','belum bayar') %>?');">
<div class="card-bd"> <div class="card-bd">
<div style="display:flex;gap:14px;flex-wrap:wrap"> <div style="display:flex;gap:14px;flex-wrap:wrap">
<div class="fg"><label class="fl"><i class="bi bi-calendar-event"></i> <%= t('admin.bc.isolir_date','Tanggal Isolir') %></label> <div class="fg"><label class="fl"><i class="bi bi-calendar-event"></i> <%= t('admin.bc.isolir_date','Tanggal Isolir') %></label>
@@ -145,7 +145,7 @@
</div> </div>
</div> </div>
</div> </div>
<div style="font-size:11px;color:var(--muted);margin-top:6px"><i class="bi bi-info-circle"></i> Kirim ke pelanggan <b>belum bayar</b> pada tanggal isolir terpilih. Gunakan <b>Random</b> agar tidak dianggap spam/di-ban.</div> <div style="font-size:11px;color:var(--muted);margin-top:6px"><i class="bi bi-info-circle"></i><%= t('admin.bc.send_to',' Kirim ke pelanggan ') %><b><%= t('admin.bc.unpaid','belum bayar') %></b><%= t('admin.bc.on_date',' pada tanggal isolir terpilih. Gunakan ') %><b>Random</b> agar tidak dianggap spam/di-ban.</div>
</div> </div>
<div class="card-bd" style="border-top:1px solid var(--border);background:var(--bg2);display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap"> <div class="card-bd" style="border-top:1px solid var(--border);background:var(--bg2);display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap">
<div id="bsResult" style="font-size:12px" class="text-muted"></div> <div id="bsResult" style="font-size:12px" class="text-muted"></div>
+1 -1
View File
@@ -32,7 +32,7 @@
<h6><i class="bi bi-list-ul"></i> <%= t('admin.col.list','Daftar Kolektor') %></h6> <h6><i class="bi bi-list-ul"></i> <%= t('admin.col.list','Daftar Kolektor') %></h6>
</div> </div>
<div class="hint" style="padding:12px 16px 0 16px;font-size:12px;color:var(--muted)"> <div class="hint" style="padding:12px 16px 0 16px;font-size:12px;color:var(--muted)">
Pengaturan approval pembayaran berlaku per akun kolektor. `<%= t('admin.col.direct','Langsung Lunas') %>` berarti pembayaran dari kolektor langsung melunasi invoice, sedangkan `<%= t('admin.col.approval','Perlu Approval') %>` berarti harus disetujui admin atau kasir terlebih dahulu. Pengaturan approval pembayaran berlaku per akun kolektor. `<%= t('admin.col.direct','Langsung Lunas') %><%= t('admin.col.mode_hint','` berarti pembayaran dari kolektor langsung melunasi invoice, sedangkan `') %><%= t('admin.col.approval','Perlu Approval') %>` berarti harus disetujui admin atau kasir terlebih dahulu.
</div> </div>
<div style="padding:12px 16px 0 16px;display:flex;gap:10px;flex-wrap:wrap"> <div style="padding:12px 16px 0 16px;display:flex;gap:10px;flex-wrap:wrap">
<select id="collectorModeFilter" class="fs" style="max-width:220px"> <select id="collectorModeFilter" class="fs" style="max-width:220px">
+1 -1
View File
@@ -318,7 +318,7 @@
</td> </td>
<td> <td>
<% if (c.status==='active') { %><span class="badge bs"><i class="bi bi-circle-fill" style="font-size:7px"></i><%= t('admin.customers.filter.active', 'Aktif') %></span> <% if (c.status==='active') { %><span class="badge bs"><i class="bi bi-circle-fill" style="font-size:7px"></i><%= t('admin.customers.filter.active', 'Aktif') %></span>
<% } else if (c.status==='suspended') { %><% if (c.deactivate_cause==='auto_unpaid') { %><span class="badge bd" title="<%= t('admin.cust.inactive','Nonaktif') %> otomatis: menunggak <%= c.unpaid_streak %> bulan"><i class="bi bi-person-x"></i> <%= t('admin.cust.inactive','Nonaktif') %> (auto)</span><% } else { %><span class="badge bw"><%= t('admin.customers.filter.suspended', 'Suspend') %></span><% } %> <% } else if (c.status==='suspended') { %><% if (c.deactivate_cause==='auto_unpaid') { %><span class="badge bd" title="<%= t('admin.cust.inactive','Nonaktif') %><%= t('admin.cust.reg_auto',' otomatis: menunggak ') %><%= c.unpaid_streak %> bulan"><i class="bi bi-person-x"></i> <%= t('admin.cust.inactive','Nonaktif') %> (auto)</span><% } else { %><span class="badge bw"><%= t('admin.customers.filter.suspended', 'Suspend') %></span><% } %>
<% } else { %><span class="badge bm"><%= t('admin.customers.filter.inactive', 'Nonaktif') %></span><% } %> <% } else { %><span class="badge bm"><%= t('admin.customers.filter.inactive', 'Nonaktif') %></span><% } %>
<% if (c.status === 'inactive' && (c.reg_total || c.reg_due_date || c.reg_voucher_code)) { %> <% if (c.status === 'inactive' && (c.reg_total || c.reg_due_date || c.reg_voucher_code)) { %>
<div style="font-size:10px;margin-top:4px;color:var(--muted)"> <div style="font-size:10px;margin-top:4px;color:var(--muted)">
+1 -1
View File
@@ -31,7 +31,7 @@
<% } %> <% } %>
<div class="alert alert-w mb-3"> <div class="alert alert-w mb-3">
<i class="bi bi-info-circle-fill"></i> <strong>Informasi:</strong> Halaman ini digunakan untuk mencatat kas masuk / pemasukan <strong>selain</strong> dari pembayaran tagihan pelanggan (misal: modal awal, penjualan perangkat bekas, dll). Pendapatan tagihan pelanggan sudah otomatis dicatat di laporan. <i class="bi bi-info-circle-fill"></i> <strong>Informasi:</strong><%= t('admin.cashin.info',' Halaman ini digunakan untuk mencatat kas masuk / pemasukan ') %><strong>selain</strong> dari pembayaran tagihan pelanggan (misal: modal awal, penjualan perangkat bekas, dll). Pendapatan tagihan pelanggan sudah otomatis dicatat di laporan.
</div> </div>
<% <%
+1 -1
View File
@@ -907,7 +907,7 @@
</div> </div>
<div class="mbody"> <div class="mbody">
<div id="cable-drawing-map"></div> <div id="cable-drawing-map"></div>
<p class="text-muted mt-2 small"><i class="bi bi-info-circle"></i> Tarik titik oranye untuk mengubah jalur. Klik tombol di bawah untuk menambah titik lekukan.</p> <p class="text-muted mt-2 small"><i class="bi bi-info-circle"></i> <%= t('admin.map.route_hint','Tarik titik oranye untuk mengubah jalur. Klik tombol di bawah untuk menambah titik lekukan.') %></p>
</div> </div>
<div class="mf"> <div class="mf">
<button type="button" class="btn btn-sm btn-outline-primary" onclick="addMorePoints()"><i class="bi bi-plus-circle"></i> Tambah Titik Lekukan</button> <button type="button" class="btn btn-sm btn-outline-primary" onclick="addMorePoints()"><i class="bi bi-plus-circle"></i> Tambah Titik Lekukan</button>
+3 -3
View File
@@ -190,7 +190,7 @@
<% if (typeof profileMode !== 'undefined' && profileMode === 'radius-attributes') { %> <% if (typeof profileMode !== 'undefined' && profileMode === 'radius-attributes') { %>
<i class="bi bi-broadcast"></i> <%- t('admin.pkg.mode_radius_hint','Mode RADIUS: rate-limit &amp; IP dari <b>atribut RADIUS</b> — profil PPP MikroTik tidak dibuat otomatis.') %> <i class="bi bi-broadcast"></i> <%- t('admin.pkg.mode_radius_hint','Mode RADIUS: rate-limit &amp; IP dari <b>atribut RADIUS</b> — profil PPP MikroTik tidak dibuat otomatis.') %>
<% } else { %> <% } else { %>
<i class="bi bi-router"></i> Profil PPP MikroTik akan <b>dibuat/diperbarui otomatis</b> dari speed paket<% if (typeof profileMode !== 'undefined' && profileMode === 'radius-profile') { %> (mode RADIUS pakai profil MikroTik)<% } %>. <i class="bi bi-router"></i><%= t('admin.pkg.profile_a',' Profil PPP MikroTik akan ') %><b><%= t('admin.pkg.profile_b','dibuat/diperbarui otomatis') %></b> dari speed paket<% if (typeof profileMode !== 'undefined' && profileMode === 'radius-profile') { %><%= t('admin.pkg.profile_c',' (mode RADIUS pakai profil MikroTik)') %><% } %>.
<% } %> <% } %>
</div> </div>
@@ -232,7 +232,7 @@
</div> </div>
<div class="hint" style="margin-top:6px;font-size:11px"> <div class="hint" style="margin-top:6px;font-size:11px">
<i class="bi bi-speedometer2"></i> <%- t('admin.pkg.ratelimit_hint','Rate-limit asli (MikroTik &amp; RADIUS):') %> <b class="mono" id="ratePreview" style="color:var(--primary)">-</b> <i class="bi bi-speedometer2"></i> <%- t('admin.pkg.ratelimit_hint','Rate-limit asli (MikroTik &amp; RADIUS):') %> <b class="mono" id="ratePreview" style="color:var(--primary)">-</b>
<span style="color:var(--muted)">— format <span class="mono">rx/tx</span> (+ burst: <span class="mono">rx/tx rxB/txB rxT/txT time</span>). Nilai sama dipakai untuk profil MikroTik <b>dan</b> atribut RADIUS.</span> <span style="color:var(--muted)">— format <span class="mono">rx/tx</span> (+ burst: <span class="mono">rx/tx rxB/txB rxT/txT time</span><%= t('admin.pkg.fup_hint','). Nilai sama dipakai untuk profil MikroTik ') %><b>dan</b> atribut RADIUS.</span>
</div> </div>
<!-- Kecepatan UpTo / Maximum Burst Rate (Opsional Manual) --> <!-- Kecepatan UpTo / Maximum Burst Rate (Opsional Manual) -->
@@ -260,7 +260,7 @@
</div> </div>
<div class="flex ai-c" style="gap:8px;margin-top:4px"> <div class="flex ai-c" style="gap:8px;margin-top:4px">
<input type="checkbox" name="prorate_first_invoice" id="add_prorate" value="1"> <input type="checkbox" name="prorate_first_invoice" id="add_prorate" value="1">
<label for="add_prorate" style="margin:0;font-size:12px">Prorata tagihan <strong>pertama</strong> jika pasang di tengah bulan (proporsi sisa hari, perlu tanggal pasang di data pelanggan)</label> <label for="add_prorate" style="margin:0;font-size:12px"><%= t('admin.pkg.prorata_a','Prorata tagihan ') %><strong>pertama</strong><%= t('admin.pkg.prorata_b',' jika pasang di tengah bulan (proporsi sisa hari, perlu tanggal pasang di data pelanggan)') %></label>
</div> </div>
</div> </div>
+3 -3
View File
@@ -92,7 +92,7 @@
<!-- ═══ C. REKAP PER BULAN (AUDIT) ═══ --> <!-- ═══ C. REKAP PER BULAN (AUDIT) ═══ -->
<div class="card mb16"> <div class="card mb16">
<div class="card-hd"><h6><i class="bi bi-calendar3"></i> Rekap Laba / Rugi per Bulan <%= filterYear %> <span class="text-muted" style="font-weight:400;font-size:11.5px">— export &amp; cetak tiap bulan</span></h6></div> <div class="card-hd"><h6><i class="bi bi-calendar3"></i> Rekap Laba / Rugi per Bulan <%= filterYear %> <span class="text-muted" style="font-weight:400;font-size:11.5px"><%- t('admin.rep.export_hint','— export &amp; cetak tiap bulan') %></span></h6></div>
<div class="tbl-wrap"> <div class="tbl-wrap">
<table class="dtbl"> <table class="dtbl">
<thead> <thead>
@@ -150,7 +150,7 @@
</table> </table>
</div> </div>
<div class="card-bd" style="padding-top:0"> <div class="card-bd" style="padding-top:0">
<div class="hint"><i class="bi bi-info-circle"></i> Baris <b>TOTAL</b> = penjumlahan tiap bulan; cocokkan dengan <b>Laporan Laba / Rugi</b> (tahun) di atas untuk audit.</div> <div class="hint"><i class="bi bi-info-circle"></i> Baris <b>TOTAL</b><%= t('admin.rep.sum_hint',' = penjumlahan tiap bulan; cocokkan dengan ') %><b>Laporan Laba / Rugi</b><%= t('admin.rep.audit_hint',' (tahun) di atas untuk audit.') %></div>
</div> </div>
</div> </div>
@@ -215,7 +215,7 @@
<!-- ═══ G. PENGATURAN PAJAK & RETRIBUSI (collapsible) ═══ --> <!-- ═══ G. PENGATURAN PAJAK & RETRIBUSI (collapsible) ═══ -->
<details class="card"> <details class="card">
<summary class="card-hd"><h6 style="margin:0"><i class="bi bi-percent"></i> Pengaturan Pajak &amp; Retribusi <span class="text-muted" style="font-weight:400;font-size:11.5px">— dihitung otomatis</span></h6></summary> <summary class="card-hd"><h6 style="margin:0"><i class="bi bi-percent"></i> Pengaturan Pajak &amp; Retribusi <span class="text-muted" style="font-weight:400;font-size:11.5px"><%= t('admin.rep.calc_hint','— dihitung otomatis') %></span></h6></summary>
<div class="card-bd"> <div class="card-bd">
<form method="POST" action="/admin/reports/finance"> <form method="POST" action="/admin/reports/finance">
<div class="form-row" style="grid-template-columns:repeat(auto-fit,minmax(160px,1fr))"> <div class="form-row" style="grid-template-columns:repeat(auto-fit,minmax(160px,1fr))">
+10 -10
View File
@@ -222,7 +222,7 @@
<div class="fg"> <div class="fg">
<label class="fl"><%= t('admin.set.public_url','URL Publik Aplikasi (Public Base URL)') %></label> <label class="fl"><%= t('admin.set.public_url','URL Publik Aplikasi (Public Base URL)') %></label>
<input class="fc" name="public_base_url" value="<%= settings.public_base_url || '' %>" placeholder="https://portal.domain-anda.com"> <input class="fc" name="public_base_url" value="<%= settings.public_base_url || '' %>" placeholder="https://portal.domain-anda.com">
<div style="font-size:10px; color:var(--muted); margin-top:4px">Digunakan untuk menyusun tautan login pelanggan yang dikirim via WhatsApp. Contoh: <span class="mono">https://portal.domain-anda.com</span></div> <div style="font-size:10px; color:var(--muted); margin-top:4px"><%= t('admin.set.link_hint','Digunakan untuk menyusun tautan login pelanggan yang dikirim via WhatsApp. Contoh: ') %><span class="mono">https://portal.domain-anda.com</span></div>
</div> </div>
<div class="fg"> <div class="fg">
<label class="fl"><%= t('admin.set.id_prefix','Prefix ID Pelanggan') %></label> <label class="fl"><%= t('admin.set.id_prefix','Prefix ID Pelanggan') %></label>
@@ -445,7 +445,7 @@
<div class="fg"> <div class="fg">
<label class="fl"><%= t('admin.set.wa_numbers','Nomor Admin WA (Pisahkan dengan koma)') %></label> <label class="fl"><%= t('admin.set.wa_numbers','Nomor Admin WA (Pisahkan dengan koma)') %></label>
<input class="fc" name="whatsapp_admin_numbers" value="<%= (settings.whatsapp_admin_numbers || []).join(', ') %>" placeholder="6281xxx, 6282xxx"> <input class="fc" name="whatsapp_admin_numbers" value="<%= (settings.whatsapp_admin_numbers || []).join(', ') %>" placeholder="6281xxx, 6282xxx">
<div style="font-size:10px; color:var(--muted); margin-top:4px">Nomor admin akan menerima notifikasi monitoring sistem. Nomor teknisi diambil otomatis dari <a href="/admin/technicians" style="color:var(--brand);font-weight:600">Data Teknisi</a>.</div> <div style="font-size:10px; color:var(--muted); margin-top:4px"><%= t('admin.set.wa_numbers_hint','Nomor admin akan menerima notifikasi monitoring sistem. Nomor teknisi diambil otomatis dari ') %><a href="/admin/technicians" style="color:var(--brand);font-weight:600">Data Teknisi</a>.</div>
</div> </div>
<div class="fg"> <div class="fg">
<label class="fl"><%= t('admin.set.wa_delay','Jeda Antar Pesan Broadcast (Detik)') %></label> <label class="fl"><%= t('admin.set.wa_delay','Jeda Antar Pesan Broadcast (Detik)') %></label>
@@ -465,7 +465,7 @@
<div style="display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap"> <div style="display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap">
<div style="flex:1 1 200px"> <div style="flex:1 1 200px">
<input class="fc" type="file" id="brand_portal_file" accept="image/png,image/jpeg,image/jpg,image/webp"> <input class="fc" type="file" id="brand_portal_file" accept="image/png,image/jpeg,image/jpg,image/webp">
<div style="font-size:10px; color:var(--muted); margin-top:4px">PNG/JPG/WebP, maks 2MB. PNG/JPEG otomatis dioptimalkan. Dipakai di halaman login &amp; portal pelanggan.</div> <div style="font-size:10px; color:var(--muted); margin-top:4px"><%- t('admin.set.logo_hint','PNG/JPG/WebP, maks 2MB. PNG/JPEG otomatis dioptimalkan. Dipakai di halaman login &amp; portal pelanggan.') %></div>
</div> </div>
<button class="btn btn-s" type="button" onclick="uploadBrandLogo('portal', this)" style="height:38px"><i class="bi bi-upload"></i> Upload</button> <button class="btn btn-s" type="button" onclick="uploadBrandLogo('portal', this)" style="height:38px"><i class="bi bi-upload"></i> Upload</button>
</div> </div>
@@ -495,7 +495,7 @@
<div style="display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap"> <div style="display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap">
<div style="flex:1 1 200px"> <div style="flex:1 1 200px">
<input class="fc" type="file" id="brand_pwa_file" accept="image/png,image/jpeg,image/jpg,image/webp"> <input class="fc" type="file" id="brand_pwa_file" accept="image/png,image/jpeg,image/jpg,image/webp">
<div style="font-size:10px; color:var(--muted); margin-top:4px">Setelah upload, ikon PWA (192/512/maskable/apple-touch) &amp; favicon otomatis dibuat ulang dari gambar ini. Sebaiknya gambar persegi (1:1).</div> <div style="font-size:10px; color:var(--muted); margin-top:4px"><%- t('admin.set.pwa_icon_hint','Setelah upload, ikon PWA (192/512/maskable/apple-touch) &amp; favicon otomatis dibuat ulang dari gambar ini. Sebaiknya gambar persegi (1:1).') %></div>
</div> </div>
<button class="btn btn-s" type="button" onclick="uploadBrandLogo('pwa', this)" style="height:38px"><i class="bi bi-upload"></i> Upload</button> <button class="btn btn-s" type="button" onclick="uploadBrandLogo('pwa', this)" style="height:38px"><i class="bi bi-upload"></i> Upload</button>
<button class="btn btn-g" type="button" onclick="regenPwaIcons(this)" style="height:38px"><i class="bi bi-phone"></i> Buat Ulang Ikon</button> <button class="btn btn-g" type="button" onclick="regenPwaIcons(this)" style="height:38px"><i class="bi bi-phone"></i> Buat Ulang Ikon</button>
@@ -517,7 +517,7 @@
<div style="display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap"> <div style="display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap">
<div style="flex:1 1 200px"> <div style="flex:1 1 200px">
<input class="fc" type="file" id="brand_hero_file" accept="image/png,image/jpeg,image/jpg,image/webp"> <input class="fc" type="file" id="brand_hero_file" accept="image/png,image/jpeg,image/jpg,image/webp">
<div style="font-size:10px; color:var(--muted); margin-top:4px">PNG/JPEG otomatis dikonversi ke <b>WebP</b> (+ fallback JPEG) agar lebih ringan. Rasio disarankan 16:10 (mis. 1600×1000).</div> <div style="font-size:10px; color:var(--muted); margin-top:4px"><%= t('admin.set.hero_hint','PNG/JPEG otomatis dikonversi ke ') %><b>WebP</b> (+ fallback JPEG) agar lebih ringan. Rasio disarankan 16:10 (mis. 1600×1000).</div>
</div> </div>
<button class="btn btn-s" type="button" onclick="uploadBrandLogo('hero', this)" style="height:38px"><i class="bi bi-upload"></i> Upload</button> <button class="btn btn-s" type="button" onclick="uploadBrandLogo('hero', this)" style="height:38px"><i class="bi bi-upload"></i> Upload</button>
</div> </div>
@@ -548,7 +548,7 @@
<div class="fg"> <div class="fg">
<label class="fl"><%= t('admin.set.speed_link','Link Speed Test') %></label> <label class="fl"><%= t('admin.set.speed_link','Link Speed Test') %></label>
<input class="fc" name="speedtest_url" id="speedtest_url_input" value="<%= settings.speedtest_url || '' %>" placeholder="https://speed.domain-anda.com"> <input class="fc" name="speedtest_url" id="speedtest_url_input" value="<%= settings.speedtest_url || '' %>" placeholder="https://speed.domain-anda.com">
<div style="font-size:10px; color:var(--muted); margin-top:4px">Bisa <b>internal</b> (LibreSpeed / OpenSpeedTest self-hosted) atau <b>eksternal</b> (Ookla Custom, Cloudflare, Google Fiber, dll). Harus bisa diakses <b>public</b>. Jika dikosongkan, dashboard pelanggan menampilkan <i>"Speedtest belum di setup"</i>.</div> <div style="font-size:10px; color:var(--muted); margin-top:4px">Bisa <b>internal</b><%= t('admin.set.speedtest_hint2',' (LibreSpeed / OpenSpeedTest self-hosted) atau ') %><b>eksternal</b> (Ookla Custom, Cloudflare, Google Fiber, dll). Harus bisa diakses <b>public</b><%= t('admin.set.speedtest_hint3','. Jika dikosongkan, dashboard pelanggan menampilkan ') %><i><%= t('admin.set.speedtest_notset','"Speedtest belum di setup"') %></i>.</div>
</div> </div>
<div class="fg"> <div class="fg">
<label class="fl"><%= t('admin.set.speed_mode','Mode Tampilan di Dashboard Pelanggan') %></label> <label class="fl"><%= t('admin.set.speed_mode','Mode Tampilan di Dashboard Pelanggan') %></label>
@@ -556,7 +556,7 @@
<option value="iframe" <%= (settings.speedtest_mode || 'iframe') === 'iframe' ? 'selected' : '' %>><%= t('admin.set.sp_mode_iframe','Embed / iframe — untuk provider yang mengizinkan') %></option> <option value="iframe" <%= (settings.speedtest_mode || 'iframe') === 'iframe' ? 'selected' : '' %>><%= t('admin.set.sp_mode_iframe','Embed / iframe — untuk provider yang mengizinkan') %></option>
<option value="tab" <%= settings.speedtest_mode === 'tab' ? 'selected' : '' %>><%= t('admin.set.sp_mode_tab','Buka Tab Baru — untuk provider yang memblokir iframe') %></option> <option value="tab" <%= settings.speedtest_mode === 'tab' ? 'selected' : '' %>><%= t('admin.set.sp_mode_tab','Buka Tab Baru — untuk provider yang memblokir iframe') %></option>
</select> </select>
<div style="font-size:10px; color:var(--muted); margin-top:4px">Di halaman pelanggan tersedia tombol <b>Muat Ulang</b> (reload speed test) &amp; <b>Bersihkan Cache</b>.</div> <div style="font-size:10px; color:var(--muted); margin-top:4px"><%= t('admin.set.speedtest_btn_hint','Di halaman pelanggan tersedia tombol ') %><b>Muat Ulang</b> (reload speed test) &amp; <b>Bersihkan Cache</b>.</div>
</div> </div>
<script> <script>
function applySpeedtestPreset(v) { function applySpeedtestPreset(v) {
@@ -579,12 +579,12 @@
<div class="fg"> <div class="fg">
<label class="fl"><%= t('admin.set.repo_link','Link Repositori Proyek (GitHub)') %></label> <label class="fl"><%= t('admin.set.repo_link','Link Repositori Proyek (GitHub)') %></label>
<input class="fc" name="project_url" value="<%= settings.project_url || 'https://github.com/oxpoe/open-isp' %>" placeholder="https://github.com/oxpoe/open-isp"> <input class="fc" name="project_url" value="<%= settings.project_url || 'https://github.com/oxpoe/open-isp' %>" placeholder="https://github.com/oxpoe/open-isp">
<div style="font-size:10px; color:var(--muted); margin-top:4px">Dipakai untuk tautan <b>Repo</b>, <b>Form/Issue</b> (<code>/issues/new/choose</code>), <b>Wiki</b>, dan <b>Lisensi</b> di landing page.</div> <div style="font-size:10px; color:var(--muted); margin-top:4px"><%= t('admin.set.repo_hint','Dipakai untuk tautan ') %><b>Repo</b>, <b>Form/Issue</b> (<code>/issues/new/choose</code>), <b>Wiki</b>, dan <b>Lisensi</b> di landing page.</div>
</div> </div>
<div class="fg"> <div class="fg">
<label class="fl"><%= t('admin.set.uptime_link','Link Uptime Website (Status)') %></label> <label class="fl"><%= t('admin.set.uptime_link','Link Uptime Website (Status)') %></label>
<input class="fc" name="uptime_url" value="<%= settings.uptime_url || '' %>" placeholder="https://status.domain-anda.com"> <input class="fc" name="uptime_url" value="<%= settings.uptime_url || '' %>" placeholder="https://status.domain-anda.com">
<div style="font-size:10px; color:var(--muted); margin-top:4px">Dipakai untuk tombol/redirect <b><%= t('common.status','Status') %></b> di landing page. Jika dikosongkan, landing page <b>tidak menampilkan tab Status</b>.</div> <div style="font-size:10px; color:var(--muted); margin-top:4px"><%= t('admin.set.uptime_hint','Dipakai untuk tombol/redirect ') %><b><%= t('common.status','Status') %></b> di landing page. Jika dikosongkan, landing page <b>tidak menampilkan tab Status</b>.</div>
</div> </div>
</div> </div>
</div> </div>
@@ -696,7 +696,7 @@
<div style="margin-top:10px; display:flex; gap:12px; align-items:center; flex-wrap:wrap"> <div style="margin-top:10px; display:flex; gap:12px; align-items:center; flex-wrap:wrap">
<div style="font-size:10px; color:var(--muted)">Preview:</div> <div style="font-size:10px; color:var(--muted)">Preview:</div>
<img src="<%= settings.qris_static_qr_url %>" alt="QRIS" style="height:72px; width:72px; object-fit:contain; background:#fff; border-radius:12px; padding:8px; border:1px solid rgba(0,0,0,0.08)"> <img src="<%= settings.qris_static_qr_url %>" alt="QRIS" style="height:72px; width:72px; object-fit:contain; background:#fff; border-radius:12px; padding:8px; border:1px solid rgba(0,0,0,0.08)">
<div style="font-size:10px; color:var(--muted)">Gunakan tombol <span style="font-weight:700">QR</span> di menu Billing untuk membuat nominal unik per invoice.</div> <div style="font-size:10px; color:var(--muted)">Gunakan tombol <span style="font-weight:700">QR</span><%= t('admin.set.qris_unique_hint',' di menu Billing untuk membuat nominal unik per invoice.') %></div>
</div> </div>
<% } %> <% } %>
+4 -4
View File
@@ -87,10 +87,10 @@
</div> </div>
<div class="fg mb12"> <div class="fg mb12">
<label class="fl">API Endpoint URL (Default: <code>https://api.fonnte.com/send</code> atau URL Server Self-Hosted)</label> <label class="fl">API Endpoint URL (Default: <code>https://api.fonnte.com/send</code><%= t('admin.wa.selfhost',' atau URL Server Self-Hosted)') %></label>
<input class="fc" name="fonnte_api_url" value="<%= fonnteSettings.fonnte_api_url %>" placeholder="https://api.fonnte.com/send"> <input class="fc" name="fonnte_api_url" value="<%= fonnteSettings.fonnte_api_url %>" placeholder="https://api.fonnte.com/send">
<div style="font-size: 11px; color: var(--muted); margin-top: 4px;"> <div style="font-size: 11px; color: var(--muted); margin-top: 4px;">
<i class="bi bi-info-circle"></i> Gunakan <code>https://api.fonnte.com/send</code> untuk Fonnte Cloud, atau ubah ke domain/IP server mandiri jika menggunakan <b>Fonnte Self-Hosted</b> (misal: <code>http://192.168.1.100:8000/send</code>). <i class="bi bi-info-circle"></i> Gunakan <code>https://api.fonnte.com/send</code><%= t('admin.wa.fonnte_hint',' untuk Fonnte Cloud, atau ubah ke domain/IP server mandiri jika menggunakan ') %><b>Fonnte Self-Hosted</b> (misal: <code>http://192.168.1.100:8000/send</code>).
</div> </div>
</div> </div>
@@ -177,7 +177,7 @@
</div> </div>
<div style="margin-top:12px;padding:12px;border-radius:10px;background:rgba(173,255,47,.06);border:1px solid var(--primary-glow);"> <div style="margin-top:12px;padding:12px;border-radius:10px;background:rgba(173,255,47,.06);border:1px solid var(--primary-glow);">
<div class="fw6" style="font-size:12.5px;color:var(--primary);margin-bottom:6px;"><i class="bi bi-qr-code"></i> Login WhatsApp (Scan QR)</div> <div class="fw6" style="font-size:12.5px;color:var(--primary);margin-bottom:6px;"><i class="bi bi-qr-code"></i> Login WhatsApp (Scan QR)</div>
<div style="font-size:11.5px;color:var(--muted);margin-bottom:8px">Klik tombol, lalu buka WhatsApp &rarr; Perangkat Tertaut &rarr; Tautkan perangkat, scan QR. QR berlaku ~30 detik &amp; diperbarui otomatis.</div> <div style="font-size:11.5px;color:var(--muted);margin-bottom:8px"><%- t('admin.wa.qr_hint','Klik tombol, lalu buka WhatsApp &rarr; Perangkat Tertaut &rarr; Tautkan perangkat, scan QR. QR berlaku ~30 detik &amp; diperbarui otomatis.') %></div>
<button type="button" class="btn btn-p btn-sm" onclick="showGowaQr()"><i class="bi bi-qr-code-scan"></i> Tampilkan QR</button> <button type="button" class="btn btn-p btn-sm" onclick="showGowaQr()"><i class="bi bi-qr-code-scan"></i> Tampilkan QR</button>
<a href="<%= gowaUiUrl %>" target="_blank" rel="noopener" class="btn btn-g btn-sm" style="margin-left:6px"><i class="bi bi-box-arrow-up-right"></i> Buka UI GOWA</a> <a href="<%= gowaUiUrl %>" target="_blank" rel="noopener" class="btn btn-g btn-sm" style="margin-left:6px"><i class="bi bi-box-arrow-up-right"></i> Buka UI GOWA</a>
<div id="gowa_qr_wrap" style="display:none;margin-top:10px;text-align:center"> <div id="gowa_qr_wrap" style="display:none;margin-top:10px;text-align:center">
@@ -187,7 +187,7 @@
</div> </div>
<div style="margin-top:12px;padding:12px;border-radius:10px;background:rgba(56,189,248,0.08);border:1px solid rgba(56,189,248,0.25);"> <div style="margin-top:12px;padding:12px;border-radius:10px;background:rgba(56,189,248,0.08);border:1px solid rgba(56,189,248,0.25);">
<div class="fw6" style="font-size:12.5px;color:var(--info,#38BDF8);margin-bottom:6px;"><i class="bi bi-broadcast"></i> Webhook Pesan Masuk (Live Chat)</div> <div class="fw6" style="font-size:12.5px;color:var(--info,#38BDF8);margin-bottom:6px;"><i class="bi bi-broadcast"></i> Webhook Pesan Masuk (Live Chat)</div>
<div style="font-size:11.5px;color:var(--muted);margin-bottom:8px">Set di GOWA: <code>WHATSAPP_WEBHOOK=&lt;URL di bawah&gt;</code> dan <code>WHATSAPP_WEBHOOK_SECRET=&lt;secret&gt;</code>, lalu restart GOWA. <strong>Catatan:</strong> bot WA <em>tidak</em> membalas perintah lagi — WhatsApp hanya untuk <strong>notifikasi</strong>. Interaksi cepat (menu admin/teknisi) kini dipindah ke <strong>Telegram</strong>.</div> <div style="font-size:11.5px;color:var(--muted);margin-bottom:8px">Set di GOWA: <code>WHATSAPP_WEBHOOK=&lt;URL di bawah&gt;</code> dan <code>WHATSAPP_WEBHOOK_SECRET=&lt;secret&gt;</code>, lalu restart GOWA. <strong>Catatan:</strong> bot WA <em>tidak</em><%= t('admin.wa.notif_only',' membalas perintah lagi — WhatsApp hanya untuk ') %><strong>notifikasi</strong>. Interaksi cepat (menu admin/teknisi) kini dipindah ke <strong>Telegram</strong>.</div>
<div class="form-row mb12"> <div class="form-row mb12">
<div class="fg" style="flex:2;"> <div class="fg" style="flex:2;">
<label class="fl">Webhook URL (tempel ke GOWA)</label> <label class="fl">Webhook URL (tempel ke GOWA)</label>
+1 -1
View File
@@ -269,7 +269,7 @@ async function openChat(phone, name) {
async function loadChatMessages(phone, showSpinner = false) { async function loadChatMessages(phone, showSpinner = false) {
const container = document.getElementById('chatMessages'); const container = document.getElementById('chatMessages');
if (showSpinner) { if (showSpinner) {
container.innerHTML = `<div class="text-center text-muted" style="margin:auto;"><i class="bi bi-arrow-repeat spin"></i> Memuat pesan...</div>`; container.innerHTML = `<div class="text-center text-muted" style="margin:auto;"><i class="bi bi-arrow-repeat spin"></i><%= t('admin.wlc.loading',' Memuat pesan...') %></div>`;
} }
try { try {
+1 -1
View File
@@ -45,7 +45,7 @@
</div> </div>
<div class="alert alert-s" style="margin-bottom:14px"> <div class="alert alert-s" style="margin-bottom:14px">
<i class="bi bi-info-circle"></i> Preview semua <b>pesan otomatis</b> dengan data contoh — <b><%= sample.nama %></b> (<%= sample.id %>), paket <%= sample.paket %>. Variabel <span class="mono">{{...}}</span> diisi otomatis saat benar-benar dikirim. <i class="bi bi-info-circle"></i> Preview semua <b><%= t('admin.wp.auto','pesan otomatis') %></b><%= t('admin.wp.sample',' dengan data contoh — ') %><b><%= sample.nama %></b> (<%= sample.id %>), paket <%= sample.paket %>. Variabel <span class="mono">{{...}}</span> diisi otomatis saat benar-benar dikirim.
</div> </div>
<div class="wp-grid"> <div class="wp-grid">