Skip to main content

SOURCE CODE: My Pending and Delayed POSTs SUMMARY (Made by ChatGPT)

<!-- ====================================== -->

<!-- 1. GLOBAL STYLES (CSS)                -->
<!-- ====================================== -->
<style>
/* Font default untuk seluruh konten */
body {
    font-family: Arial, sans-serif;
}

/* Judul section utama */
.section-title {
    text-align: center;
    font-size: 26px;
    margin-bottom: 25px;
}

/* Box pembungkus modul (parent & child) */
.res-box {
    margin: auto;
    max-width: 900px;
    padding-bottom: 40px;
}

/* Search bar global */
.search-bar {
    width: 100%;
    padding: 10px;
    border-radius: 6px;
    border: 1px solid #aaa;
    margin-bottom: 15px;
}

/* Box pengaturan sort */
.sort-box {
    display: flex;
    gap: 10px;
    margin-bottom: 15px;
}
.sort-box select {
    padding: 8px;
    border-radius: 6px;
    border: 1px solid #999;
}

/* Tabel fleksibel berbasis flexbox */
.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;
}

/* Lebar kolom default (desktop) */
.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; }

/* Tombol link ke URL / postingan */
.link-btn {
    background: #0078ff;
    color: white;
    padding: 6px 12px;
    border-radius: 6px;
    text-decoration: none;
    display: inline-block;
}

/* Tombol icon edit / delete */
.icon-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 20px;
    margin: 6px;
}
.icon-edit  { color: #ffaa00; }
.icon-del   { color: #ff4444; }

/* Row dengan status DONE diberi background sedikit lebih gelap */
.done-row {
    background: #c2e6c2; /* hijau muda gelap (Opsi 3) */
}

/* Baris editor inline (muncul di bawah record ketika Edit diklik) */
.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;
}

/* Wrapper tombol di editor inline */
.inline-btn-row {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

/* Wrapper status PENDING/DONE (shaded box rapi) */
.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;
}

/* Jika status-box berada di dalam inline editor, beri jarak sedikit di atas */
.inline-edit-inner .status-box {
    margin-top: 10px;
}

/* Responsif untuk layar mobile (<768px) */
@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 (WAJIB)               -->
<!-- ====================================== -->
<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 + HELPER FUNGSI    -->
<!-- ====================================== -->
<script>
/* -------------------------------------------------
   3.1. Inisialisasi Firebase
   ------------------------------------------------- */
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);

/* Referensi Realtime Database */
const rdb      = firebase.database();
const refCRUD  = rdb.ref("urlCrudData");   // Parent: Pending / Delayed Posts
const refAPP   = rdb.ref("webAppData");    // Child : TO DO List

/* -------------------------------------------------
   3.2. HELPER TANGGAL & JAM
   ------------------------------------------------- */

/* Membuat timestamp 24 jam: YYYY-MM-DD HH:MM:SS */
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;
}

/* Dipakai untuk header "Today: ..." */
function todayLong() {
    return new Date().toLocaleString("en-US", {
        month: "short",
        day: "2-digit",
        year: "numeric"
    });
}

/* Jika data lama hanya berisi tanggal (tanpa jam), tambahkan 00:00:00 */
function formatDateWithFallback(val) {
    if (!val) return "";
    if (val.includes(":")) return val;
    return val + " 00:00:00";
}

/* Parse tanggal aman untuk sorting (kalau gagal, kembalikan 0) */
function parseDateSafe(val) {
    if (!val) return 0;
    const t = Date.parse(val);
    if (!isNaN(t)) return t;
    return 0;
}

/* -------------------------------------------------
   3.3. HELPER JUDUL OTOMATIS UNTUK URL BLOG
   ------------------------------------------------- */

function isBlog(url) { 
    return url.includes("ronin1985.blogspot.com");
}

/* Mengubah slug terakhir URL blog menjadi judul "Title Case" */
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;
    }
}

/* Jika bukan blog sendiri, pakai domain utama sebagai judul kasar */
function guessTitle(url) {
    return url.replace("https://", "").replace("http://", "").split("/")[0];
}
</script>

<!-- ====================================== -->
<!-- 4. PARENT MODULE — urlCrudData         -->
<!--    (My Pending and Delayed POSTs)     -->
<!-- ====================================== -->

<div class="res-box">
    <h2 class="section-title" id="todayHeader"></h2>
    <h2 style="text-align: center;">🔗 My Pending and Delayed POSTs SUMMARY</h2>

    <!-- 4.1. Search + Sort -->
    <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 value="desc" selected>Descending (Newest First)</option>
        </select>
    </div>

    <!-- 4.2. MAIN FORM (tetap di atas) -->
    <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 />
        <!-- Status utama PENDING / DONE di main form parent -->
        <div class="status-box">
            <label>
                <input type="radio" name="statusCRUD" value="PENDING" checked /> Pending
            </label>
            <label>
                <input type="radio" name="statusCRUD" 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>

    <!-- 4.3. FLEX TABLE PARENT -->
    <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>

        <!-- Isi tabel di-render lewat JavaScript -->
        <div id="tableBodyCRUD"></div>
    </div>
</div>

<!-- ====================================== -->
<!-- 5. PARENT LOGIC (JS)                  -->
<!-- ====================================== -->
<script>
/* Header "Today: ..." */
document.getElementById("todayHeader").innerHTML = "📅 Today: " + todayLong();

/* Array penampung data parent dari Firebase */
let crudData = [];
let currentEditID_CRUD = null;  // ID record yang sedang diedit (parent)

/* --------------------------------------------- */
/* 5.1. Realtime listener ke node urlCrudData    */
/* --------------------------------------------- */
refCRUD.on("value", function(snap){
    crudData = snap.val() ? Object.values(snap.val()) : [];
    renderCRUD();
});

/* Event search & sort */
document.getElementById("searchCRUD").oninput   = renderCRUD;
document.getElementById("sortByCRUD").onchange  = renderCRUD;
document.getElementById("sortOrderCRUD").onchange = renderCRUD;

/* Ambil status terpilih di MAIN FORM parent */
function getStatusCRUD(){
    const radios = document.querySelectorAll('input[name="statusCRUD"]');
    for (const r of radios) {
        if (r.checked) return r.value;
    }
    return "PENDING";
}

/* Cari record parent berdasarkan id */
function findCRUDById(id){
    return crudData.find(function(e){ return e.id === id; }) || null;
}

/* Menutup semua inline editor parent */
function closeAllInlineCRUD(){
    crudData.forEach(function(e){
        const box = document.getElementById("inlineCRUD-" + e.id);
        if (box) box.style.display = "none";
    });
}

/* --------------------------------------------- */
/* 5.2. Render daftar parent ke tabel            */
/* --------------------------------------------- */
function renderCRUD(){
    let search = document.getElementById("searchCRUD").value.toLowerCase();
    let sortBy = document.getElementById("sortByCRUD").value;
    let order  = document.getElementById("sortOrderCRUD").value;

    /* Filter berdasarkan search (autoTitle, myTitle, url) */
    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);
    });

    /* Sorting – default sortBy=date (timestamp) */
    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);
        }
    });

    /* Bangun HTML row + inline editor */
    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>"

        /* INLINE EDITOR – muncul di bawah record */
        + "<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;
}

/* --------------------------------------------- */
/* 5.3. ADD (main form parent)                   */
/* --------------------------------------------- */
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(),   // timestamp baru
        autoTitle: autoT,
        myTitle:   myT,
        url:       url,
        status:    status
    });

    /* Reset form */
    document.getElementById("urlInputCRUD").value    = "";
    document.getElementById("myTitleInputCRUD").value = "";
}

/* --------------------------------------------- */
/* 5.4. OPEN INLINE EDITOR PARENT                */
/* --------------------------------------------- */
function openInlineCRUD(id){
    const rec = findCRUDById(id);
    if (!rec) return;

    currentEditID_CRUD = id;

    /* Tutup semua editor lain dulu */
    closeAllInlineCRUD();

    const box = document.getElementById("inlineCRUD-" + id);
    if (box) box.style.display = "block";

    /* Set nilai field inline dari record */
    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"));
    });

    /* Optional: sinkronkan juga ke MAIN FORM atas */
    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);
    });
}

/* Tutup satu inline editor parent */
function closeInlineCRUD(id){
    const box = document.getElementById("inlineCRUD-" + id);
    if (box) box.style.display = "none";
}

/* --------------------------------------------- */
/* 5.5. SAVE EDIT via MAIN FORM (parent)         */
/* --------------------------------------------- */
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
    });

    /* Reset state & form */
    currentEditID_CRUD = null;
    document.getElementById("urlInputCRUD").value     = "";
    document.getElementById("myTitleInputCRUD").value = "";
    document.getElementById("saveBtnCRUD").style.display = "none";
    closeAllInlineCRUD();
}

/* --------------------------------------------- */
/* 5.6. SAVE EDIT via INLINE EDITOR (parent)     */
/* --------------------------------------------- */
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
    });

    /* Reset main form dan tutup inline */
    currentEditID_CRUD = null;
    document.getElementById("urlInputCRUD").value     = "";
    document.getElementById("myTitleInputCRUD").value = "";
    document.getElementById("saveBtnCRUD").style.display = "none";
    closeInlineCRUD(id);
}

/* --------------------------------------------- */
/* 5.7. DELETE PARENT RECORD                     */
/* --------------------------------------------- */
function deleteCRUD(id){
    if (confirm("Delete this entry?")){
        refCRUD.child(id).remove();
    }
}
</script>

<!-- ====================================== -->
<!-- 6. CHILD MODULE — webAppData           -->
<!--    (TO DO LIST SUMMARY)               -->
<!-- ====================================== -->

<div class="res-box">
    <h2 class="section-title" style="text-align: center;">📝 TO DO LIST SUMMARY</h2>

    <!-- 6.1. Search + Sort -->
    <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 value="desc" selected>Descending (Newest First)</option>
        </select>
    </div>

    <!-- 6.2. MAIN FORM CHILD -->
    <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 />
        <!-- Status utama PENDING / DONE di main form child -->
        <div class="status-box">
            <label>
                <input type="radio" name="statusAPP" value="PENDING" checked /> Pending
            </label>
            <label>
                <input type="radio" name="statusAPP" 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>

    <!-- 6.3. FLEX TABLE CHILD -->
    <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>

        <!-- Isi tabel child di-render lewat JS -->
        <div id="tableBodyAPP"></div>
    </div>
</div>

<!-- ====================================== -->
<!-- 7. CHILD LOGIC (JS)                    -->
<!-- ====================================== -->
<script>
let appData           = [];
let currentEditID_APP = null;  // ID record yang sedang diedit (child)

/* --------------------------------------------- */
/* 7.1. Realtime listener ke node webAppData     */
/* --------------------------------------------- */
refAPP.on("value", function(snap){
    appData = snap.val() ? Object.values(snap.val()) : [];
    renderAPP();
});

/* Event search & sort (child) */
document.getElementById("searchAPP").oninput   = renderAPP;
document.getElementById("sortByAPP").onchange  = renderAPP;
document.getElementById("sortOrderAPP").onchange = renderAPP;

/* Ambil status terpilih di MAIN FORM child */
function getStatusAPP(){
    const radios = document.querySelectorAll('input[name="statusAPP"]');
    for (const r of radios) {
        if (r.checked) return r.value;
    }
    return "PENDING";
}

/* Cari record child berdasarkan id */
function findAPPById(id){
    return appData.find(function(e){ return e.id === id; }) || null;
}

/* Tutup semua inline editor child */
function closeAllInlineAPP(){
    appData.forEach(function(e){
        const box = document.getElementById("inlineAPP-" + e.id);
        if (box) box.style.display = "none";
    });
}

/* Helper kecil untuk ambil properti by key */
function eVal(obj, key){
    return obj[key];
}

/* --------------------------------------------- */
/* 7.2. Render daftar child ke tabel             */
/* --------------------------------------------- */
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>"

        /* INLINE EDITOR CHILD */
        + "<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;
}

/* --------------------------------------------- */
/* 7.3. ADD (main child form)                    */
/* --------------------------------------------- */
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(),   // timestamp baru
        header: h,
        detail: d,
        status: status
    });

    document.getElementById("headerInputAPP").value = "";
    document.getElementById("detailInputAPP").value = "";
}

/* --------------------------------------------- */
/* 7.4. OPEN INLINE EDITOR CHILD                 */
/* --------------------------------------------- */
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"));
    });

    /* Sinkron ke MAIN FORM child */
    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);
    });
}

/* Tutup satu inline editor child */
function closeInlineAPP(id){
    const box = document.getElementById("inlineAPP-" + id);
    if (box) box.style.display = "none";
}

/* --------------------------------------------- */
/* 7.5. SAVE EDIT via MAIN FORM (child)          */
/* --------------------------------------------- */
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();
}

/* --------------------------------------------- */
/* 7.6. SAVE EDIT via INLINE CHILD               */
/* --------------------------------------------- */
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);
}

/* --------------------------------------------- */
/* 7.7. DELETE CHILD RECORD                      */
/* --------------------------------------------- */
function deleteAPP(id){
    if (confirm("Delete entry?")){
        refAPP.child(id).remove();
    }
}
</script>

Comments

Popular posts from this blog

[ERROR BUG]
ChatGPT+Gemini: TikTok → Blogger Embed Converter using Cloudflare/Online Server

🔄 Refresh Page ERROR BUG: The connection is blocked because it was initiated by a public page to connect to devices or servers on your local network. Planning: Revise Program CODE Code USING Javascript/Online Server Code NOT USING Javascript Sample Working Code aka Already Repaired! Temporary Solution is by Asking AI Assistant to do REPAIR CODE of (Not yet Repaired) Current Conversion Program Code-Output TikTok Archive – Embedded Preview TikTok Embed ▶ View this video on TikTok ⚠️ DISCLAIMER: INPUT URL LIMITATION This program is currently restricted to processing Full Browser URLs only. It does not support TikTok’s mobile "short-link" format (e.g., vt.tiktok.com ). Required Action: Users must open the video in a web browser and copy the expanded URL from the address bar before pasting it into this program. URL Conversion Example ❌ UNSUPPORTED: https://vt.tiktok.com/ZSaXoFyov/ ✅ REQ...

Repost! Web-Based to Android Apps Convertion (MEDIAN.CO etc.)

CONTOH HASIL Android APK "PROGRAM" SAMPLE: Youtube and Instagram EMBEDded to Blogger/Blogspot.com SOURCE CODE Click this box to download Contoh Sample SHORTCUT-APPs "precise" click to download : median.co R8: ronin1985.blogspot.com R2M: ronin-manu.blogspot.com Gw udah coba Median.co utk mengubah Website gw menjadi Aplikasi Android Keren bet!! Median.co Cekidot Software lain yg mirip! ChatGPT : If you're looking for tools similar to Median.co to convert websites into Android apps, here are some top alternatives, especially for no-code or low-code users: 🔧 Best Tools Like Median.co to Convert Website to Android Apps 1. WebViewGold Platform: Windows/macOS (Xcode/Android Studio) Key Feature: Converts any website into iOS/Android app via WebView. Pros: One-time purchase Custom splash screen, push notifications Can open external links in external browser Cons:  Requi...

REPOST: Studying WATER PUMP by ROMAN ENGINEERING

*^ Ini yg Asli Gan! Mekanisme pada Concrete Pump: Kok ky Sistem Mekanik Romawi ya?! Tapi malah bisa HANYA pake PER aka bukan "MATA BOR look a like" Mekanisme Drill yg Cost Pembuatan bikin REPOT aka harus Tool SUPER Khusus Dari Material Besi yg digunakan terlihat langsung secara kasat mata Jauh Lebih Banyak drpd Per Biasa seperti yg ditunjukkan pd Video Alternatif dgn Penggunaan PER Video dr Instagram: Source: YouTube Rome's drainage machines #history #romanempire #engineering