<!--CHILD FINAL SNIPPET (webAppData)-->
<!--======================================-->
<!--1. GLOBAL STYLES (CSS)-->
<!--======================================-->
<style>
body {
font-family: Arial, sans-serif;
}
.section-title {
text-align: center;
font-size: 26px;
margin-bottom: 25px;
}
.res-box {
margin: auto;
max-width: 900px;
padding-bottom: 40px;
}
.search-bar {
width: 100%;
padding: 10px;
border-radius: 6px;
border: 1px solid #aaa;
margin-bottom: 15px;
}
.sort-box {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.sort-box select {
padding: 8px;
border-radius: 6px;
border: 1px solid #999;
}
.responsive-table {
width: 100%;
display: block;
}
.responsive-row {
display: flex;
flex-wrap: wrap;
border-bottom: 1px solid #ccc;
padding: 10px 0;
}
.responsive-head {
background: #eee;
font-weight: bold;
}
.col-date { flex: 0 0 18%; max-width: 18%; padding: 5px; }
.col-status { flex: 0 0 10%; max-width: 10%; padding: 5px; text-align:center; }
.col-auto { flex: 0 0 26%; max-width: 26%; padding: 5px; }
.col-my { flex: 0 0 26%; max-width: 26%; padding: 5px; }
.col-url { flex: 0 0 10%; max-width: 10%; padding: 5px; text-align:center; }
.col-act { flex: 0 0 10%; max-width: 10%; padding: 5px; text-align:center; }
.link-btn {
background: #0078ff;
color: white;
padding: 6px 12px;
border-radius: 6px;
text-decoration: none;
display: inline-block;
}
.icon-btn {
background: none;
border: none;
cursor: pointer;
font-size: 20px;
margin: 6px;
}
.icon-edit { color: #ffaa00; }
.icon-del { color: #ff4444; }
.done-row {
background: #c2e6c2;
}
.inline-edit-row {
display: none;
width: 100%;
box-sizing: border-box;
}
.inline-edit-inner {
background: #f4f4f4;
border: 1px solid #ccc;
border-radius: 8px;
padding: 10px;
margin: 8px 0 4px 0;
}
.inline-edit-inner b {
display: block;
margin-bottom: 6px;
}
.inline-edit-inner input,
.inline-edit-inner textarea,
.inline-edit-inner select {
width: 100%;
padding: 6px;
margin: 4px 0 10px 0;
box-sizing: border-box;
border-radius: 5px;
border: 1px solid #aaa;
}
.inline-btn-row {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.status-box {
display: flex;
align-items: center;
gap: 25px;
padding: 8px 14px;
border: 1px solid #ccc;
border-radius: 10px;
background: #f5f5f5;
width: max-content;
margin: 6px 0;
}
.status-box label {
display: flex;
align-items: center;
gap: 6px;
font-size: 15px;
white-space: nowrap;
}
.inline-edit-inner .status-box {
margin-top: 10px;
}
@media (max-width: 768px) {
.col-date,
.col-status,
.col-auto,
.col-my,
.col-url,
.col-act {
flex: 0 0 100%;
max-width: 100%;
text-align: left;
}
}
</style>
<!--======================================-->
<!--2. FIREBASE CDN-->
<!--======================================-->
<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>
<!--======================================-->
<!--3. FIREBASE CONFIG + HELPERS-->
<!--======================================-->
<script>
const firebaseConfig = {
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"
};
if (!firebase.apps.length) firebase.initializeApp(firebaseConfig);
const rdb = firebase.database();
const refCRUD = rdb.ref("urlCrudData");
const refAPP = rdb.ref("webAppData");
function nowDateTime() {
const d = new Date();
const yy = d.getFullYear();
const mm = String(d.getMonth() + 1).padStart(2, "0");
const dd = String(d.getDate()).padStart(2, "0");
const hh = String(d.getHours()).padStart(2, "0");
const mi = String(d.getMinutes()).padStart(2, "0");
const ss = String(d.getSeconds()).padStart(2, "0");
return yy + "-" + mm + "-" + dd + " " + hh + ":" + mi + ":" + ss;
}
function todayLong() {
return new Date().toLocaleString("en-US", {
month: "short",
day: "2-digit",
year: "numeric"
});
}
function formatDateWithFallback(val) {
if (!val) return "";
if (val.includes(":")) return val;
return val + " 00:00:00";
}
function parseDateSafe(val) {
if (!val) return 0;
const t = Date.parse(val);
if (!isNaN(t)) return t;
return 0;
}
function isBlog(url) {
return url.includes("ronin1985.blogspot.com");
}
function blogTitle(url) {
try {
let last = url.split("/").pop().replace(".html", "");
return last
.replace(/[-_]+/g, " ")
.replace(/\b\w/g, function(m){ return m.toUpperCase(); });
} catch (e) {
return url;
}
}
function guessTitle(url) {
return url.replace("https://", "").replace("http://", "").split("/")[0];
}
</script>
<!--======================================-->
<!--CHILD MODULE — webAppData (To-Do)-->
<!--======================================-->
<div class="res-box">
<h2 class="section-title" style="text-align: center;">📝 TO DO LIST SUMMARY</h2>
<input class="search-bar" id="searchAPP" placeholder="Search header or detail…" />
<div class="sort-box">
<select id="sortByAPP">
<option value="date">Sort by Date</option>
<option value="header">Sort by Header</option>
<option value="detail">Sort by Detail</option>
</select>
<select id="sortOrderAPP">
<option value="asc">Ascending</option>
<option selected="" value="desc">Descending (Newest First)</option>
</select>
</div>
<div style="background: rgb(244, 244, 244); border-radius: 10px; border: 1px solid rgb(204, 204, 204); margin-bottom: 25px; padding: 15px;">
<b>Header (Title):</b><br />
<input id="headerInputAPP" style="border-radius: 6px; border: 1px solid rgb(170, 170, 170); margin-top: 5px; padding: 10px; width: 100%;" />
<br /><br />
<b>Detail:</b><br />
<textarea id="detailInputAPP" style="border-radius: 6px; border: 1px solid rgb(170, 170, 170); height: 80px; margin-top: 5px; padding: 10px; width: 100%;"></textarea>
<br /><br />
<b>Status:</b><br />
<div class="status-box">
<label>
<input checked="" name="statusAPP" type="radio" value="PENDING" /> Pending
</label>
<label>
<input name="statusAPP" type="radio" value="DONE" /> Done
</label>
</div>
<br />
<button onclick="addAPP()" style="background: rgb(0, 120, 255); border-radius: 8px; border: none; color: white; padding: 10px 18px;">
➕ ADD ENTRY
</button>
<button id="saveBtnAPP" onclick="saveAPP()" style="background: rgb(255, 170, 0); border-radius: 8px; border: none; color: white; display: none; padding: 10px 18px;">
💾 SAVE EDIT (MAIN FORM)
</button>
</div>
<div class="responsive-table">
<div class="responsive-row responsive-head">
<div class="col-date">DATE / TIME</div>
<div class="col-auto">HEADER</div>
<div class="col-my">DETAIL</div>
<div class="col-status">STATUS</div>
<div class="col-act">ACTIONS</div>
</div>
<div id="tableBodyAPP"></div>
</div>
</div>
<script>
let appData = [];
let currentEditID_APP = null;
refAPP.on("value", function(snap){
appData = snap.val() ? Object.values(snap.val()) : [];
renderAPP();
});
document.getElementById("searchAPP").oninput = renderAPP;
document.getElementById("sortByAPP").onchange = renderAPP;
document.getElementById("sortOrderAPP").onchange= renderAPP;
function getStatusAPP(){
const radios = document.querySelectorAll('input[name="statusAPP"]');
for (const r of radios) {
if (r.checked) return r.value;
}
return "PENDING";
}
function findAPPById(id){
return appData.find(function(e){ return e.id === id; }) || null;
}
function closeAllInlineAPP(){
appData.forEach(function(e){
const box = document.getElementById("inlineAPP-" + e.id);
if (box) box.style.display = "none";
});
}
function eVal(obj, key){
return obj[key];
}
function renderAPP(){
let search = document.getElementById("searchAPP").value.toLowerCase();
let sortBy = document.getElementById("sortByAPP").value;
let order = document.getElementById("sortOrderAPP").value;
let data = appData.filter(function(e){
const h = (e.header || "").toLowerCase();
const d = (e.detail || "").toLowerCase();
return h.includes(search) || d.includes(search);
});
data.sort(function(a,b){
if (sortBy === "date") {
const x = parseDateSafe(a.date);
const y = parseDateSafe(b.date);
return (order === "asc") ? (x - y) : (y - x);
} else {
let x = (eVal(a, sortBy) || "").toString().toLowerCase();
let y = (eVal(b, sortBy) || "").toString().toLowerCase();
return (order === "asc") ? x.localeCompare(y) : y.localeCompare(x);
}
});
let html = "";
data.forEach(function(e){
const statusLabel = e.status || "PENDING";
const rowClass = (statusLabel === "DONE") ? "responsive-row done-row" : "responsive-row";
html += ""
+ "<div class='" + rowClass + "'>"
+ "<div class='col-date'>" + formatDateWithFallback(e.date) + "</div>"
+ "<div class='col-auto' style='font-weight:bold;'>" + (e.header || "") + "</div>"
+ "<div class='col-my'>" + (e.detail || "") + "</div>"
+ "<div class='col-status'><span>" + statusLabel + "</span></div>"
+ "<div class='col-act'>"
+ "<button class='icon-btn icon-edit' onclick=\"openInlineAPP('" + e.id + "')\">✏</button>"
+ "<button class='icon-btn icon-del' onclick=\"deleteAPP('" + e.id + "')\">🗑</button>"
+ "</div>"
+ "</div>"
+ "<div class='inline-edit-row' id='inlineAPP-" + e.id + "'>"
+ "<div class='inline-edit-inner'>"
+ "<b>Inline Edit — TO DO ITEM</b>"
+ "<label>Header (Title):</label>"
+ "<input id='inlineAPP-header-" + e.id + "' value='" + (e.header || "") + "' />"
+ "<label>Detail:</label>"
+ "<textarea id='inlineAPP-detail-" + e.id + "'>" + (e.detail || "") + "</textarea>"
+ "<label>Status:</label>"
+ "<div class='status-box'>"
+ "<label><input type='radio' name='inlineAPP-status-" + e.id + "' value='PENDING'" + (statusLabel === "PENDING" ? " checked" : "") + "> Pending</label>"
+ "<label><input type='radio' name='inlineAPP-status-" + e.id + "' value='DONE'" + (statusLabel === "DONE" ? " checked" : "") + "> Done</label>"
+ "</div>"
+ "<div class='inline-btn-row'>"
+ "<button onclick=\"saveInlineAPP('" + e.id + "')\">💾 Save Inline</button>"
+ "<button onclick=\"closeInlineAPP('" + e.id + "')\">✖ Cancel</button>"
+ "</div>"
+ "</div>"
+ "</div>";
});
document.getElementById("tableBodyAPP").innerHTML = html;
}
function addAPP(){
let h = document.getElementById("headerInputAPP").value.trim();
let d = document.getElementById("detailInputAPP").value.trim();
if (!h){
alert("Header cannot be empty.");
return;
}
let key = refAPP.push().key;
let status = getStatusAPP();
refAPP.child(key).set({
id: key,
date: nowDateTime(),
header: h,
detail: d,
status: status
});
document.getElementById("headerInputAPP").value = "";
document.getElementById("detailInputAPP").value = "";
}
function openInlineAPP(id){
const rec = findAPPById(id);
if (!rec) return;
currentEditID_APP = id;
closeAllInlineAPP();
const box = document.getElementById("inlineAPP-" + id);
if (box) box.style.display = "block";
const hInput = document.getElementById("inlineAPP-header-" + id);
const dInput = document.getElementById("inlineAPP-detail-" + id);
if (hInput) hInput.value = rec.header || "";
if (dInput) dInput.value = rec.detail || "";
const radios = document.querySelectorAll("input[name='inlineAPP-status-" + id + "']");
radios.forEach(function(r){
r.checked = (r.value === (rec.status || "PENDING"));
});
document.getElementById("headerInputAPP").value = rec.header || "";
document.getElementById("detailInputAPP").value = rec.detail || "";
document.getElementById("saveBtnAPP").style.display = "inline-block";
const mainStatus = rec.status || "PENDING";
const mainRadios = document.querySelectorAll('input[name="statusAPP"]');
mainRadios.forEach(function(r){
r.checked = (r.value === mainStatus);
});
}
function closeInlineAPP(id){
const box = document.getElementById("inlineAPP-" + id);
if (box) box.style.display = "none";
}
function saveAPP(){
if (!currentEditID_APP){
alert("No record selected for editing (click ✏ Edit first).");
return;
}
let h = document.getElementById("headerInputAPP").value.trim();
let d = document.getElementById("detailInputAPP").value.trim();
let status = getStatusAPP();
refAPP.child(currentEditID_APP).update({
header: h,
detail: d,
status: status
});
currentEditID_APP = null;
document.getElementById("headerInputAPP").value = "";
document.getElementById("detailInputAPP").value = "";
document.getElementById("saveBtnAPP").style.display = "none";
closeAllInlineAPP();
}
function saveInlineAPP(id){
const hInput = document.getElementById("inlineAPP-header-" + id);
const dInput = document.getElementById("inlineAPP-detail-" + id);
if (!hInput || !dInput) return;
const radios = document.querySelectorAll("input[name='inlineAPP-status-" + id + "']");
let st = "PENDING";
radios.forEach(function(r){
if (r.checked) st = r.value;
});
const h = hInput.value.trim();
const d = dInput.value.trim();
refAPP.child(id).update({
header: h,
detail: d,
status: st
});
currentEditID_APP = null;
document.getElementById("headerInputAPP").value = "";
document.getElementById("detailInputAPP").value = "";
document.getElementById("saveBtnAPP").style.display = "none";
closeInlineAPP(id);
}
function deleteAPP(id){
if (confirm("Delete entry?")){
refAPP.child(id).remove();
}
}
</script>
Comments