SAMPLE (SOURCE-CODE) of
DATA INSERTION PROGRAM for
"TO-DO_LIST RELATED APP: JOB DONE" POST
that utilizes FIREBASE DATABASE
<!-- ============================================== -->
<!-- FIREBASE CDN (BLOGGER COMPAT MODE) -->
<!-- ============================================== -->
<script src="https://www.gstatic.com/firebasejs/9.22.2/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.22.2/firebase-database-compat.js"></script>
<script>
firebase.initializeApp({
apiKey: "AIzaSyAF5r6Rvu-DmoV-vf47wYTZfarpVGmNYR0",
authDomain: "ronin-11938.firebaseapp.com",
databaseURL: "https://ronin-11938-default-rtdb.firebaseio.com",
projectId: "ronin-11938",
storageBucket: "ronin-11938.firebasestorage.app",
messagingSenderId: "823368889742",
appId: "1:823368889742:web:609e16ace214b94a0df"
});
var rdb = firebase.database();
var refCRUD = rdb.ref("JobDoneData");
</script>
<!-- ============================================== -->
<!-- XLSX (SheetJS) FOR EXCEL PARSING -->
<!-- ============================================== -->
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
<!-- ============================================== -->
<!-- STYLE (BLOGGER SAFE) -->
<!-- ============================================== -->
<style>
:root {
--bg: #ffffff;
--card: #ffffff;
--glass: rgba(255,255,255,0.82);
--border: #dcdcdc;
--shadow: rgba(0,0,0,0.14);
--text: #222;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #111;
--card: #1b1b1b;
--glass: rgba(40,40,40,0.72);
--border: #333;
--shadow: rgba(0,0,0,0.55);
--text: #f5f5f5;
}
}
body { background: var(--bg); color: var(--text); }
.crud-box, .table-box, .import-box {
background: var(--card);
border:1px solid var(--border);
padding:18px;
border-radius:12px;
margin-bottom:20px;
box-shadow:0 4px 12px var(--shadow);
}
input, select, textarea {
padding:10px;
width:100%;
border-radius:8px;
border:1px solid var(--border);
box-shadow:0 1px 3px var(--shadow) inset;
}
.icon-btn {
cursor:pointer;
font-size:22px;
margin-right:6px;
transition:0.15s;
}
.icon-btn:hover { transform:scale(1.28); }
/* DATE COLUMN — TWO ROWS (Month + Year) */
.date-cell {
width: 150px;
min-width: 150px;
text-align: center;
white-space: normal;
line-height: 1.25;
font-weight: 600;
}
.date-year {
display:block;
font-size:0.9em;
margin-top:2px;
color:#666;
}
.glass-modal {
position:fixed; top:0; left:0; width:100%; height:100%;
background:rgba(0,0,0,0.55);
display:none;
align-items:center;
justify-content:center;
z-index:9999;
}
.glass-modal-content {
background:var(--glass);
backdrop-filter:blur(12px);
padding:20px;
border-radius:15px;
width:90%;
max-width:420px;
box-shadow:0 4px 18px var(--shadow);
color:var(--text);
}
.table-clean { border-collapse:collapse; width:100%; }
.table-clean th {
background:#eaeaea;
padding:10px;
border-bottom:2px solid var(--border);
}
.table-clean td {
padding:10px;
border-bottom:1px solid var(--border);
}
.table-clean tr:nth-child(even) {
background:rgba(0,0,0,0.04);
}
/* Import area */
#importLog, #importPreview {
width:100%;
padding:10px;
border-radius:8px;
border:1px solid var(--border);
font-family:Consolas,"Courier New",monospace;
font-size:12px;
white-space:pre-wrap;
background:#fafafa;
max-height:160px;
overflow-y:auto;
}
</style>
<!-- ============================================== -->
<!-- INPUT FORM -->
<!-- ============================================== -->
<div class="crud-box">
<h3>➕ Add Job Done</h3>
<label><b>Month</b></label>
<select id="monthSelect" style="margin-bottom:10px;"></select>
<label><b>Year</b></label>
<select id="yearSelect" style="margin-bottom:10px;"></select>
<label><b>Description</b></label>
<textarea id="jobDesc" style="height:80px;"></textarea>
<br><br>
<button onclick="addRecord()"
style="padding:10px 20px; border-radius:8px; border:none; background:#007bff; color:white;">
💾 Save
</button>
</div>
<!-- ============================================== -->
<!-- DB STATUS + IMPORT FROM SPREADSHEET -->
<!-- ============================================== -->
<div class="import-box">
<h3>📂 Database Status & Spreadsheet Sync (Full Replace)</h3>
<p>
Current records in <b>JobDoneData</b>:
<span id="dbCount" style="font-weight:bold;">0</span>
</p>
<hr>
<p><b>Step 1.</b> Choose Excel file (.xls / .xlsx) with columns:<br>
<i>Month | Year | Description</i> (Month will be stored EXACTLY as in the file)</p>
<input type="file" id="fileInput" accept=".xls,.xlsx" />
<br><br>
<button onclick="parseSpreadsheet()" style="padding:8px 16px;">📄 Read Spreadsheet</button>
<br><br>
<p><b>Parsed preview:</b></p>
<div id="importPreview">No data parsed yet.</div>
<br>
<button id="syncBtn" onclick="syncToFirebase()" style="padding:8px 16px;" disabled>
⚠️ FULL REPLACE: Sync Spreadsheet → Firebase
</button>
<p style="color:red; margin-top:8px;">
WARNING: This will <b>delete all existing records</b> in JobDoneData and replace them with rows from the spreadsheet.
</p>
<h4>Log</h4>
<div id="importLog">Waiting...</div>
</div>
<!-- ============================================== -->
<!-- SEARCH + SORT -->
<!-- ============================================== -->
<div class="crud-box">
<label><b>Search</b></label>
<input id="searchInput" onkeyup="renderTable()" placeholder="Search description or date…" />
<br><br>
<label><b>Sort by</b></label>
<select id="sortSelect" onchange="renderTable()">
<option value="desc">Newest First</option>
<option value="asc">Oldest First</option>
</select>
</div>
<!-- ============================================== -->
<!-- TABLE -->
<!-- ============================================== -->
<div class="table-box">
<table class="table-clean">
<thead>
<tr>
<th>Date</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody id="dataList"></tbody>
</table>
</div>
<!-- ============================================== -->
<!-- CUSTOM FLOATING GLASS MODAL -->
<!-- ============================================== -->
<div id="modalGlass" class="glass-modal">
<div class="glass-modal-content">
<h4 id="modalTitle"></h4>
<div id="modalBody" style="white-space:pre-wrap; margin-top:10px;"></div>
<br>
<button onclick="closeModal()" style="padding:8px 15px;">Close</button>
</div>
</div>
<!-- ============================================== -->
<!-- LOGIC SCRIPT -->
<!-- ============================================== -->
<script>
var allData = [];
var importedRows = []; // from spreadsheet
var monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"];
/* -------------------------- UTILITIES -------------------------- */
function logImport(msg) {
var area = document.getElementById("importLog");
area.textContent += "\n" + msg;
area.scrollTop = area.scrollHeight;
}
function isYear(str) {
return /^[0-9]{4}$/.test(str);
}
/* Parse month RAW → { num, label }
- label: EXACTLY as in file (trimmed), except empty → "UNDATED MONTH"
- num: used ONLY for sortKey (0 if cannot detect) */
function parseMonthRaw(monthRaw) {
if (monthRaw == null || String(monthRaw).trim() === "") {
return { num: 0, label: "UNDATED MONTH" };
}
var label = String(monthRaw).trim(); // what we SHOW
var lower = label.toLowerCase().replace(/\./g, "");
// try numeric: 1..12
var digitsOnly = label.replace(/[^\d]/g, "");
if (digitsOnly !== "") {
var n = parseInt(digitsOnly, 10);
if (!isNaN(n) && n >= 1 && n <= 12) {
return { num: n, label: label };
}
}
// try to map known Indonesian/English names to numeric, but KEEP label as-is
var dict = {
"januari": 1, "january": 1,
"februari": 2, "pebruari": 2, "february": 2,
"maret": 3, "march": 3,
"april": 4,
"mei": 5, "may": 5,
"juni": 6, "june": 6,
"juli": 7, "july": 7,
"agustus": 8, "august": 8,
"september": 9,
"oktober": 10, "october": 10,
"november": 11,
"desember": 12, "december": 12
};
if (dict[lower]) {
return { num: dict[lower], label: label };
}
// fuzzy first 3 chars for numeric only
var p3 = lower.substring(0,3);
var fuzzy = {
"jan": 1,
"feb": 2,
"peb": 2,
"mar": 3,
"apr": 4,
"mei": 5,
"may": 5,
"jun": 6,
"jul": 7,
"agu": 8,
"ags": 8,
"aug": 8,
"sep": 9,
"okt": 10,
"oct": 10,
"nov": 11,
"des": 12,
"dse": 12,
"dec": 12
};
if (fuzzy[p3]) {
return { num: fuzzy[p3], label: label };
}
// if completely unknown → keep label, num=0
return { num: 0, label: label };
}
/* -------------------------- LOAD MONTH & YEAR SELECTS (MANUAL INPUT) -------------------------- */
function loadMonthYear() {
var mSel = document.getElementById("monthSelect");
var ySel = document.getElementById("yearSelect");
var today = new Date();
var curM = today.getMonth() + 1;
var curY = today.getFullYear();
var i, y;
for (i=1; i<=12; i++) {
var o = document.createElement("option");
o.value = i;
o.text = monthNames[i-1];
if (i === curM) o.selected = true;
mSel.appendChild(o);
}
for (y=2023; y<=curY+5; y++) {
var o2 = document.createElement("option");
o2.value = y;
o2.text = y;
if (y === curY) o2.selected = true;
ySel.appendChild(o2);
}
}
loadMonthYear();
/* -------------------------- ADD RECORD (MANUAL FORM) -------------------------- */
function addRecord() {
var m = document.getElementById("monthSelect").value;
var y = document.getElementById("yearSelect").value;
var desc = document.getElementById("jobDesc").value;
if (m==="" || y==="" || desc.trim()==="") {
alert("Please complete all fields.");
return;
}
var id = refCRUD.push().key;
var monthNum = parseInt(m,10);
var monthLabel = monthNames[monthNum-1] || "UNDATED MONTH";
var yearNum = parseInt(y,10);
var sortKey = yearNum * 100 + monthNum;
refCRUD.child(id).set({
id:id,
month:monthNum,
monthName:monthLabel, // manual entries use English labels
year:yearNum,
description:desc,
sortKey:sortKey
});
document.getElementById("jobDesc").value="";
}
/* -------------------------- FIREBASE LISTENER (DB STATUS + TABLE) -------------------------- */
refCRUD.on("value", function(snap) {
allData=[];
snap.forEach(function(c){ allData.push(c.val()); });
document.getElementById("dbCount").textContent = allData.length;
renderTable();
});
/* -------------------------- RENDER TABLE -------------------------- */
function renderTable() {
var tbody=document.getElementById("dataList");
tbody.innerHTML="";
var search=document.getElementById("searchInput").value.toLowerCase();
var sort=document.getElementById("sortSelect").value;
var filtered=[];
var i;
for (i=0;i<allData.length;i++){
var d = allData[i];
var monthLabel = d.monthName ? d.monthName : "UNDATED MONTH";
var text = monthLabel + " " + d.year + " " + (d.description || "");
if (text.toLowerCase().indexOf(search)!==-1) filtered.push(d);
}
filtered.sort(function(a,b){
return sort==="desc" ? b.sortKey-a.sortKey : a.sortKey-b.sortKey;
});
for (i=0;i<filtered.length;i++){
var d2=filtered[i];
var tr=document.createElement("tr");
var mLabel = d2.monthName ? d2.monthName : "UNDATED MONTH";
tr.innerHTML =
"<td class='date-cell'>" +
mLabel +
"<span class='date-year'>" + d2.year + "</span>" +
"</td>" +
"<td>"+ (d2.description || "").replace(/\n/g,'<br>') +"</td>" +
"<td>" +
"<span class='icon-btn' onclick=\"viewRecord('" + d2.id + "')\">👁️</span>" +
"<span class='icon-btn' onclick=\"editRecord('" + d2.id + "')\">✏️</span>" +
"<span class='icon-btn' onclick=\"deleteRecord('" + d2.id + "')\">🗑️</span>" +
"</td>";
tbody.appendChild(tr);
}
}
/* -------------------------- VIEW / EDIT / DELETE -------------------------- */
function viewRecord(id){
var i;
for (i=0;i<allData.length;i++){
if (allData[i].id===id){
var d = allData[i];
var monthLabel = d.monthName ? d.monthName : "UNDATED MONTH";
openModal(
"View Record",
monthLabel + " " + d.year + "\n\n" + (d.description || "")
);
return;
}
}
}
function editRecord(id){
var i;
for (i=0;i<allData.length;i++){
if (allData[i].id===id){
var newDesc=prompt("Edit description:", allData[i].description || "");
if (newDesc!==null){
refCRUD.child(id).update({description:newDesc});
}
return;
}
}
}
function deleteRecord(id){
if (confirm("Delete this record?")){
refCRUD.child(id).remove();
}
}
function openModal(title,body){
document.getElementById("modalTitle").textContent=title;
document.getElementById("modalBody").textContent=body;
document.getElementById("modalGlass").style.display="flex";
}
function closeModal(){
document.getElementById("modalGlass").style.display="none";
}
/* -------------------------- PARSE SPREADSHEET (UPLOAD) -------------------------- */
function parseSpreadsheet() {
var fileInput = document.getElementById("fileInput");
var preview = document.getElementById("importPreview");
var syncBtn = document.getElementById("syncBtn");
document.getElementById("importLog").textContent = "Reading file...";
importedRows = [];
syncBtn.disabled = true;
if (!fileInput.files || !fileInput.files[0]) {
alert("Please choose an Excel file first.");
document.getElementById("importLog").textContent = "No file selected.";
return;
}
var file = fileInput.files[0];
var reader = new FileReader();
reader.onload = function(e) {
try {
var data = e.target.result;
var workbook = XLSX.read(data, { type:"binary" });
var firstSheetName = workbook.SheetNames[0];
var sheet = workbook.Sheets[firstSheetName];
var rows = XLSX.utils.sheet_to_json(sheet, { header:1 });
var ok = 0;
var skip = 0;
var previewLines = [];
for (var i=0; i<rows.length; i++) {
var r = rows[i];
if (!r || r.length === 0) continue;
// STRICT: Column-1 = Month, Column-2 = Year, Column-3+ = Description
var cMonth = r[0] != null ? String(r[0]) : "";
var cYear = r[1] != null ? String(r[1]).trim() : "";
var desc = "";
if (r.length >= 3) {
for (var j=2; j<r.length; j++) {
if (r[j] != null) {
if (desc !== "") desc += " ";
desc += String(r[j]);
}
}
}
if (!isYear(cYear)) {
// probably header row or bad row
skip++;
continue;
}
if (!desc || desc.trim() === "") {
skip++;
continue;
}
var yearNum = parseInt(cYear,10);
var mInfo = parseMonthRaw(cMonth);
var monthNum = mInfo.num;
var monthLabel = mInfo.label;
var sortKey = yearNum * 100 + monthNum;
importedRows.push({
month: monthNum,
monthName: monthLabel, // EXACT text from file (or UNDATED MONTH)
year: yearNum,
description: desc,
sortKey: sortKey
});
ok++;
if (previewLines.length < 10) {
previewLines.push(monthLabel + " " + yearNum + " — " + desc);
}
}
preview.textContent = "Parsed rows: " + ok + " (skipped: " + skip + ")\n\n" +
previewLines.join("\n");
document.getElementById("importLog").textContent = "Spreadsheet parsed. Ready to sync.";
if (ok > 0) {
syncBtn.disabled = false;
}
} catch (err) {
document.getElementById("importLog").textContent = "Error reading file: " + err;
preview.textContent = "Error.";
}
};
reader.readAsBinaryString(file);
}
/* -------------------------- SYNC (FULL REPLACE) -------------------------- */
function syncToFirebase() {
if (!importedRows || importedRows.length === 0) {
alert("No parsed data to sync. Please read spreadsheet first.");
return;
}
if (!confirm("This will DELETE ALL existing records and replace with " +
importedRows.length + " rows from spreadsheet. Continue?")) {
return;
}
document.getElementById("importLog").textContent = "Clearing JobDoneData...";
refCRUD.remove(function(err) {
if (err) {
document.getElementById("importLog").textContent = "Error clearing DB: " + err;
return;
}
document.getElementById("importLog").textContent = "DB cleared. Inserting rows...";
for (var i=0; i<importedRows.length; i++) {
var rec = importedRows[i];
var newRef = refCRUD.push();
newRef.set({
id: newRef.key,
month: rec.month,
monthName: rec.monthName,
year: rec.year,
description: rec.description,
sortKey: rec.sortKey
});
}
document.getElementById("importLog").textContent += "\nSync complete. Inserted " +
importedRows.length + " rows.";
document.getElementById("syncBtn").disabled = true;
// FINAL count from DB
refCRUD.once("value", function(snap) {
var count = snap.numChildren();
document.getElementById("importLog").textContent +=
"\nFINAL COUNT after sync: " + count + " records.";
document.getElementById("dbCount").textContent = count;
});
});
}
</script>
Comments
Post a Comment