diff --git a/locales/en.json b/locales/en.json
index 208bc92..edd4171 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -1781,7 +1781,73 @@
"mayar_webhook": "Mayar Webhook (Mode 1 — Gateway)",
"save": "Save Settings",
"pay_mode": "Mode",
- "qris_note": "Note"
+ "qris_note": "Note",
+ "no_data": "No data yet.",
+ "ew_not_tx": "Not a transaction",
+ "ew_paid": "Paid",
+ "ew_unmatched": "Not matched"
+ },
+ "mt": {
+ "updating": "Updating...",
+ "online_list": "Online PPPoE Users",
+ "offline_list": "Offline PPPoE Users",
+ "limit_uptime": "Uptime Limit (e.g. 1h)"
+ },
+ "disc": {
+ "title": "Invoice Discount",
+ "settings_title": "Discount Settings",
+ "opt_selected": "Selected customers (batch)",
+ "opt_single": "Single customer (1 by 1)",
+ "opt_all": "All active customers",
+ "pick_customer": "Select Customer",
+ "ph_pick_customer": "-- select customer --",
+ "disc_type": "Discount Type",
+ "opt_percent": "Percent (%)",
+ "opt_amount": "Amount (Rp)",
+ "disc_value": "Discount Value",
+ "ph_value": "e.g. 10 (percent) / 20000 (Rp)",
+ "disc_hint": "The percentage is calculated from the base price (before VAT/USO).",
+ "period_month": "Billing Period — Month",
+ "year": "Year",
+ "note_opt": "Note (optional)",
+ "ph_note": "e.g. independence month promo",
+ "apply_hint": "The discount applies to unpaid invoices in the selected period. Paid/void invoices are unchanged. The QRIS unique code is regenerated from the new amount.",
+ "apply": "Apply Discount",
+ "selected": "selected",
+ "history": "Discount History (last 100)",
+ "th_note": "Note"
+ },
+ "cpay": {
+ "title": "Collector Payment Approval",
+ "list": "Request List",
+ "opt_all": "All Processes",
+ "opt_pending": "Needs Approval",
+ "opt_auto": "Collector Auto",
+ "opt_admin": "Approved by Admin",
+ "opt_cashier": "Approved by Cashier",
+ "opt_rejected": "Manually Rejected",
+ "opt_manual": "Processed Manually",
+ "th_invoice": "Invoice",
+ "th_process": "Process",
+ "approve": "Approve",
+ "reject": "Reject"
+ },
+ "att": {
+ "late_checkin": "Late Check-in",
+ "not_checkout": "Not Checked-out",
+ "all_attendance": "All Attendance",
+ "total_staff": "Total Staff",
+ "today": "Today",
+ "working": "Currently Working",
+ "checked_out": "Checked Out",
+ "late": "Late",
+ "after_0830": "After 08:30",
+ "technician": "Technician",
+ "cashier": "Cashier",
+ "checkin_time": "Check-in Time",
+ "duration": "Duration",
+ "location": "Location",
+ "photo": "Photo"
}
},
"tech": {
diff --git a/locales/id.json b/locales/id.json
index 69d9efe..2573a86 100644
--- a/locales/id.json
+++ b/locales/id.json
@@ -1782,7 +1782,73 @@
"mayar_webhook": "Webhook Mayar (Mode 1 — Gateway)",
"save": "Simpan Pengaturan",
"pay_mode": "Mode",
- "qris_note": "Catatan"
+ "qris_note": "Catatan",
+ "no_data": "Belum ada data.",
+ "ew_not_tx": "Bukan transaksi",
+ "ew_paid": "Lunas",
+ "ew_unmatched": "Belum cocok"
+ },
+ "mt": {
+ "updating": "Memperbarui...",
+ "online_list": "Daftar User PPPoE Online",
+ "offline_list": "Daftar User PPPoE Offline",
+ "limit_uptime": "Limit Uptime (e.g. 1h)"
+ },
+ "disc": {
+ "title": "Diskon Tagihan",
+ "settings_title": "Pengaturan Diskon",
+ "opt_selected": "Pelanggan terpilih (batch)",
+ "opt_single": "Satu pelanggan (1 per 1)",
+ "opt_all": "Semua pelanggan aktif",
+ "pick_customer": "Pilih Pelanggan",
+ "ph_pick_customer": "-- pilih pelanggan --",
+ "disc_type": "Tipe Diskon",
+ "opt_percent": "Persen (%)",
+ "opt_amount": "Nominal (Rp)",
+ "disc_value": "Nilai Diskon",
+ "ph_value": "mis. 10 (persen) / 20000 (Rp)",
+ "disc_hint": "Persen dihitung dari harga dasar (sebelum PPN/USO).",
+ "period_month": "Periode Tagihan — Bulan",
+ "year": "Tahun",
+ "note_opt": "Catatan (opsional)",
+ "ph_note": "mis. promo bulan kemerdekaan",
+ "apply_hint": "Diskon berlaku ke tagihan belum bayar pada periode terpilih. Tagihan lunas/hangus tidak diubah. Kode unik QRIS akan digenerate ulang dari nominal baru.",
+ "apply": "Terapkan Diskon",
+ "selected": "dipilih",
+ "history": "Riwayat Diskon (100 terakhir)",
+ "th_note": "Catatan"
+ },
+ "cpay": {
+ "title": "Approval Pembayaran Kolektor",
+ "list": "Daftar Request",
+ "opt_all": "Semua Proses",
+ "opt_pending": "Butuh Approval",
+ "opt_auto": "Auto Kolektor",
+ "opt_admin": "Disetujui Admin",
+ "opt_cashier": "Disetujui Kasir",
+ "opt_rejected": "Ditolak Manual",
+ "opt_manual": "Diproses Manual",
+ "th_invoice": "Tagihan",
+ "th_process": "Proses",
+ "approve": "Setujui",
+ "reject": "Tolak"
+ },
+ "att": {
+ "late_checkin": "Terlambat Check-in",
+ "not_checkout": "Belum Check-out",
+ "all_attendance": "Semua Absensi",
+ "total_staff": "Total Karyawan",
+ "today": "Hari ini",
+ "working": "Sedang Bekerja",
+ "checked_out": "Sudah Pulang",
+ "late": "Terlambat",
+ "after_0830": "Setelah 08:30",
+ "technician": "Teknisi",
+ "cashier": "Kasir",
+ "checkin_time": "Waktu Check-in",
+ "duration": "Durasi",
+ "location": "Lokasi",
+ "photo": "Foto"
}
},
"tech": {
diff --git a/views/admin/attendance.ejs b/views/admin/attendance.ejs
index fc9deeb..a7ecf5f 100644
--- a/views/admin/attendance.ejs
+++ b/views/admin/attendance.ejs
@@ -131,34 +131,34 @@
-
Total Karyawan
+
<%= t('admin.att.total_staff','Total Karyawan') %>
<%= totalAll %>
-
Hari ini
+
<%= t('admin.att.today','Hari ini') %>
-
Sedang Bekerja
+
<%= t('admin.att.working','Sedang Bekerja') %>
<%= checkedInAll %>
Check-in
-
Sudah Pulang
+
<%= t('admin.att.checked_out','Sudah Pulang') %>
<%= checkedOutAll %>
Check-out
-
Terlambat
+
<%= t('admin.att.late','Terlambat') %>
<%= lateCheckIns.length %>
-
Setelah 08:30
+
<%= t('admin.att.after_0830','Setelah 08:30') %>
-
Teknisi
+
<%= t('admin.att.technician','Teknisi') %>
<%= techStats.total || 0 %>
<%= techStats.checked_in || 0 %> aktif •
@@ -174,7 +174,7 @@
-
Kasir
+
<%= t('admin.att.cashier','Kasir') %>
<%= cashierStats.total || 0 %>
<%= cashierStats.checked_in || 0 %> aktif •
@@ -195,7 +195,7 @@
<% if (lateCheckIns.length > 0) { %>
-
Terlambat Check-in (<%= lateCheckIns.length %>)
+ <%= t('admin.att.late_checkin','Terlambat Check-in') %> (<%= lateCheckIns.length %>)
@@ -203,8 +203,8 @@
<%= t('pwa.field.name','Nama') %>
<%= t('common.type','Tipe') %>
- Waktu Check-in
- Terlambat
+ <%= t('admin.att.checkin_time','Waktu Check-in') %>
+ <%= t('admin.att.late','Terlambat') %>
@@ -231,7 +231,7 @@
<% if (notCheckedOut.length > 0) { %>
-
Belum Check-out (<%= notCheckedOut.length %>)
+ <%= t('admin.att.not_checkout','Belum Check-out') %> (<%= notCheckedOut.length %>)
@@ -240,7 +240,7 @@
<%= t('pwa.field.name','Nama') %>
<%= t('common.type','Tipe') %>
Check-in
- Durasi
+ <%= t('admin.att.duration','Durasi') %>
@@ -267,7 +267,7 @@
-
Semua Absensi - <%= parseDateInTimezone(selectedDate).toLocaleDateString('id-ID', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) %>
+ <%= t('admin.att.all_attendance','Semua Absensi') %> - <%= parseDateInTimezone(selectedDate).toLocaleDateString('id-ID', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) %>
<% if (attendances.length > 0) { %>
@@ -279,10 +279,10 @@
<%= t('common.type','Tipe') %>
Check-in
Check-out
-
Durasi
+
<%= t('admin.att.duration','Durasi') %>
<%= t('common.status','Status') %>
-
Lokasi
-
Foto
+
<%= t('admin.att.location','Lokasi') %>
+
<%= t('admin.att.photo','Foto') %>
<%= t('common.actions','Aksi') %>
@@ -336,13 +336,13 @@
<% if (att.check_in_photo) { %>
-
+
In
<% } %>
<% if (att.check_out_photo) { %>
-
+
Out
<% } %>
@@ -402,7 +402,7 @@
}
function editAttendance(id) {
- const checkIn = prompt('Waktu Check-in (YYYY-MM-DD HH:MM:SS):');
+ const checkIn = prompt('<%= t('admin.att.checkin_time','Waktu Check-in') %> (YYYY-MM-DD HH:MM:SS):');
if (!checkIn) return;
const checkOut = prompt('Waktu Check-out (YYYY-MM-DD HH:MM:SS) - kosongkan jika belum:');
diff --git a/views/admin/collector_payments.ejs b/views/admin/collector_payments.ejs
index 0aba021..d0863d2 100644
--- a/views/admin/collector_payments.ejs
+++ b/views/admin/collector_payments.ejs
@@ -2,7 +2,7 @@
-
Approval Pembayaran Kolektor | <%= company %>
+
<%= t('admin.cpay.title','Approval Pembayaran Kolektor') %> | <%= company %>
@@ -14,7 +14,7 @@
- Approval Pembayaran Kolektor
+ <%= t('admin.cpay.title','Approval Pembayaran Kolektor') %>
<%= (typeof session !== 'undefined' && session.isCashier) ? t('admin.role.cashier', 'Kasir') : t('admin.role.admin', 'Admin') %>
@@ -31,20 +31,20 @@
-
Daftar Request (<%= (rows || []).length %>)
+ <%= t('admin.cpay.list','Daftar Request') %> (<%= (rows || []).length %>)
Label proses membantu membedakan request yang memang menunggu approval, request yang otomatis langsung lunas oleh kolektor, dan request yang sudah diproses admin atau kasir.
- Semua Proses
- Butuh Approval
- Auto Kolektor
- Disetujui Admin
- Disetujui Kasir
- Ditolak Manual
- Diproses Manual
+ <%= t('admin.cpay.opt_all','Semua Proses') %>
+ <%= t('admin.cpay.opt_pending','Butuh Approval') %>
+ <%= t('admin.cpay.opt_auto','Auto Kolektor') %>
+ <%= t('admin.cpay.opt_admin','Disetujui Admin') %>
+ <%= t('admin.cpay.opt_cashier','Disetujui Kasir') %>
+ <%= t('admin.cpay.opt_rejected','Ditolak Manual') %>
+ <%= t('admin.cpay.opt_manual','Diproses Manual') %>
@@ -54,9 +54,9 @@
<%= t('collector.col_time','Waktu') %>
<%= t('collector.role','Kolektor') %>
<%= t('common.customer','Pelanggan') %>
- Tagihan
+ <%= t('admin.cpay.th_invoice','Tagihan') %>
<%= t('collector.col_amount','Nominal') %>
- Proses
+ <%= t('admin.cpay.th_process','Proses') %>
<%= t('common.status','Status') %>
<%= t('common.actions','Aksi') %>
@@ -119,12 +119,12 @@
diff --git a/views/admin/discounts.ejs b/views/admin/discounts.ejs
index ae24762..a98c6c1 100644
--- a/views/admin/discounts.ejs
+++ b/views/admin/discounts.ejs
@@ -2,7 +2,7 @@
-
Diskon Tagihan | <%= company %>
+
<%= t('admin.disc.title','Diskon Tagihan') %> | <%= company %>