<!--PARENT FINAL SNIPPET (urlCrudData)-->
<!--======================================-->
<!--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>
<!--======================================-->
<!--PARENT MODULE — urlCrudData (Pending)-->
<!--======================================-->
<div class="res-box">
<h2 class="section-title" id="todayHeader"></h2>
<h2 style="text-align: center;">🔗 My Pending and Delayed POSTs SUMMARY</h2>
<input class="search-bar" id="searchCRUD" placeholder="Search title or URL…" />
<div class="sort-box">
<select id="sortByCRUD">
<option value="date">Sort by Date</option>
<option value="autoTitle">Sort by Auto Title</option>
<option value="myTitle">Sort by My Title</option>
</select>
<select id="sortOrderCRUD">
<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>Insert URL:</b><br />
<input id="urlInputCRUD" style="border-radius: 6px; border: 1px solid rgb(170, 170, 170); margin-top: 5px; padding: 10px; width: 100%;" />
<br /><br />
<b>Your Own Title (Optional):</b><br />
<input id="myTitleInputCRUD" style="border-radius: 6px; border: 1px solid rgb(170, 170, 170); margin-top: 5px; padding: 10px; width: 100%;" />
<br /><br />
<b>Status:</b><br />
<div class="status-box">
<label>
<input checked="" name="statusCRUD" type="radio" value="PENDING" /> Pending
</label>
<label>
<input name="statusCRUD" type="radio" value="DONE" /> Done
</label>
</div>
<br />
<button onclick="autoAddCRUD()" style="background: rgb(0, 120, 255); border-radius: 8px; border: none; color: white; padding: 10px 18px;">
➕ ADD ENTRY
</button>
<button id="saveBtnCRUD" onclick="saveEditCRUD()" 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">AUTO TITLE</div>
<div class="col-my">MY TITLE</div>
<div class="col-status">STATUS</div>
<div class="col-url">URL</div>
<div class="col-act">ACTIONS</div>
</div>
<div id="tableBodyCRUD"></div>
</div>
</div>
<script>
document.getElementById("todayHeader").innerHTML = "📅 Today: " + todayLong();
let crudData = [];
let currentEditID_CRUD = null;
refCRUD.on("value", function(snap){
crudData = snap.val() ? Object.values(snap.val()) : [];
renderCRUD();
});
document.getElementById("searchCRUD").oninput = renderCRUD;
document.getElementById("sortByCRUD").onchange = renderCRUD;
document.getElementById("sortOrderCRUD").onchange= renderCRUD;
function getStatusCRUD(){
const radios = document.querySelectorAll('input[name="statusCRUD"]');
for (const r of radios) {
if (r.checked) return r.value;
}
return "PENDING";
}
function findCRUDById(id){
return crudData.find(function(e){ return e.id === id; }) || null;
}
function closeAllInlineCRUD(){
crudData.forEach(function(e){
const box = document.getElementById("inlineCRUD-" + e.id);
if (box) box.style.display = "none";
});
}
function renderCRUD(){
let search = document.getElementById("searchCRUD").value.toLowerCase();
let sortBy = document.getElementById("sortByCRUD").value;
let order = document.getElementById("sortOrderCRUD").value;
let data = crudData.filter(function(e){
const autoT = (e.autoTitle || "").toLowerCase();
const myT = (e.myTitle || "").toLowerCase();
const url = (e.url || "").toLowerCase();
return autoT.includes(search) || myT.includes(search) || url.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 = (a[sortBy] || "").toString().toLowerCase();
let y = (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.autoTitle || "") + "</div>"
+ "<div class='col-my'>" + (e.myTitle || "") + "</div>"
+ "<div class='col-status'><span>" + statusLabel + "</span></div>"
+ "<div class='col-url'><a href='" + (e.url || "#") + "' target='_blank' class='link-btn'>LINK</a></div>"
+ "<div class='col-act'>"
+ "<button class='icon-btn icon-edit' onclick=\"openInlineCRUD('" + e.id + "')\">✏</button>"
+ "<button class='icon-btn icon-del' onclick=\"deleteCRUD('" + e.id + "')\">🗑</button>"
+ "</div>"
+ "</div>"
+ "<div class='inline-edit-row' id='inlineCRUD-" + e.id + "'>"
+ "<div class='inline-edit-inner'>"
+ "<b>Inline Edit — Pending POST</b>"
+ "<label>URL:</label>"
+ "<input id='inlineCRUD-url-" + e.id + "' value='" + (e.url || "") + "' />"
+ "<label>My Title:</label>"
+ "<input id='inlineCRUD-myTitle-" + e.id + "' value='" + (e.myTitle || "") + "' />"
+ "<label>Status:</label>"
+ "<div class='status-box'>"
+ "<label><input type='radio' name='inlineCRUD-status-" + e.id + "' value='PENDING'" + (statusLabel === "PENDING" ? " checked" : "") + "> Pending</label>"
+ "<label><input type='radio' name='inlineCRUD-status-" + e.id + "' value='DONE'" + (statusLabel === "DONE" ? " checked" : "") + "> Done</label>"
+ "</div>"
+ "<div class='inline-btn-row'>"
+ "<button onclick=\"saveInlineCRUD('" + e.id + "')\">💾 Save Inline</button>"
+ "<button onclick=\"closeInlineCRUD('" + e.id + "')\">✖ Cancel</button>"
+ "</div>"
+ "</div>"
+ "</div>";
});
document.getElementById("tableBodyCRUD").innerHTML = html;
}
function autoAddCRUD(){
let url = document.getElementById("urlInputCRUD").value.trim();
let myT = document.getElementById("myTitleInputCRUD").value.trim();
if (!url) {
alert("URL cannot be empty.");
return;
}
let autoT = isBlog(url) ? blogTitle(url) : guessTitle(url);
let key = refCRUD.push().key;
let status = getStatusCRUD();
refCRUD.child(key).set({
id: key,
date: nowDateTime(),
autoTitle: autoT,
myTitle: myT,
url: url,
status: status
});
document.getElementById("urlInputCRUD").value = "";
document.getElementById("myTitleInputCRUD").value = "";
}
function openInlineCRUD(id){
const rec = findCRUDById(id);
if (!rec) return;
currentEditID_CRUD = id;
closeAllInlineCRUD();
const box = document.getElementById("inlineCRUD-" + id);
if (box) box.style.display = "block";
const urlInput = document.getElementById("inlineCRUD-url-" + id);
const myInput = document.getElementById("inlineCRUD-myTitle-" + id);
if (urlInput) urlInput.value = rec.url || "";
if (myInput) myInput.value = rec.myTitle || "";
const radios = document.querySelectorAll("input[name='inlineCRUD-status-" + id + "']");
radios.forEach(function(r){
r.checked = (r.value === (rec.status || "PENDING"));
});
document.getElementById("urlInputCRUD").value = rec.url || "";
document.getElementById("myTitleInputCRUD").value = rec.myTitle || "";
document.getElementById("saveBtnCRUD").style.display = "inline-block";
const mainStatus = rec.status || "PENDING";
const mainRadios = document.querySelectorAll('input[name="statusCRUD"]');
mainRadios.forEach(function(r){
r.checked = (r.value === mainStatus);
});
}
function closeInlineCRUD(id){
const box = document.getElementById("inlineCRUD-" + id);
if (box) box.style.display = "none";
}
function saveEditCRUD(){
if (!currentEditID_CRUD){
alert("No record selected for editing (click ✏ Edit first).");
return;
}
let newUrl = document.getElementById("urlInputCRUD").value.trim();
let newMyT = document.getElementById("myTitleInputCRUD").value.trim();
let newStatus = getStatusCRUD();
refCRUD.child(currentEditID_CRUD).update({
url: newUrl,
myTitle: newMyT,
autoTitle: isBlog(newUrl) ? blogTitle(newUrl) : guessTitle(newUrl),
status: newStatus
});
currentEditID_CRUD = null;
document.getElementById("urlInputCRUD").value = "";
document.getElementById("myTitleInputCRUD").value = "";
document.getElementById("saveBtnCRUD").style.display = "none";
closeAllInlineCRUD();
}
function saveInlineCRUD(id){
const urlInput = document.getElementById("inlineCRUD-url-" + id);
const myInput = document.getElementById("inlineCRUD-myTitle-" + id);
if (!urlInput || !myInput) return;
const radios = document.querySelectorAll("input[name='inlineCRUD-status-" + id + "']");
let st = "PENDING";
radios.forEach(function(r){
if (r.checked) st = r.value;
});
const newUrl = urlInput.value.trim();
const newMyT = myInput.value.trim();
refCRUD.child(id).update({
url: newUrl,
myTitle: newMyT,
autoTitle: isBlog(newUrl) ? blogTitle(newUrl) : guessTitle(newUrl),
status: st
});
currentEditID_CRUD = null;
document.getElementById("urlInputCRUD").value = "";
document.getElementById("myTitleInputCRUD").value = "";
document.getElementById("saveBtnCRUD").style.display = "none";
closeInlineCRUD(id);
}
function deleteCRUD(id){
if (confirm("Delete this entry?")){
refCRUD.child(id).remove();
}
}
</script>
Comments