| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <title>Simple 10 Database</title> |
| <style> |
| :root { |
| color-scheme: light; |
| --bg: #f5f7fb; |
| --panel: #ffffff; |
| --text: #172033; |
| --muted: #61708a; |
| --line: #d9e0ec; |
| --brand: #0f766e; |
| --brand-dark: #0b5f59; |
| --danger: #b42318; |
| --shadow: 0 10px 30px rgba(28, 39, 61, 0.09); |
| } |
| |
| * { |
| box-sizing: border-box; |
| } |
| |
| body { |
| margin: 0; |
| min-height: 100vh; |
| background: var(--bg); |
| color: var(--text); |
| font-family: Arial, Helvetica, sans-serif; |
| } |
| |
| main { |
| width: min(1120px, calc(100% - 32px)); |
| margin: 0 auto; |
| padding: 28px 0; |
| } |
| |
| header { |
| display: flex; |
| justify-content: space-between; |
| gap: 20px; |
| align-items: flex-end; |
| margin-bottom: 20px; |
| } |
| |
| h1 { |
| margin: 0 0 6px; |
| font-size: 32px; |
| line-height: 1.1; |
| } |
| |
| p { |
| margin: 0; |
| color: var(--muted); |
| line-height: 1.5; |
| } |
| |
| .stats { |
| display: flex; |
| gap: 10px; |
| flex-wrap: wrap; |
| justify-content: flex-end; |
| } |
| |
| .stat { |
| min-width: 92px; |
| padding: 10px 12px; |
| background: var(--panel); |
| border: 1px solid var(--line); |
| border-radius: 8px; |
| box-shadow: var(--shadow); |
| } |
| |
| .stat strong { |
| display: block; |
| font-size: 20px; |
| } |
| |
| .stat span { |
| display: block; |
| margin-top: 2px; |
| color: var(--muted); |
| font-size: 12px; |
| text-transform: uppercase; |
| } |
| |
| .panel { |
| background: var(--panel); |
| border: 1px solid var(--line); |
| border-radius: 8px; |
| box-shadow: var(--shadow); |
| overflow: hidden; |
| } |
| |
| .toolbar { |
| display: grid; |
| grid-template-columns: 1fr auto auto; |
| gap: 12px; |
| padding: 16px; |
| border-bottom: 1px solid var(--line); |
| } |
| |
| input, |
| select { |
| width: 100%; |
| height: 42px; |
| border: 1px solid var(--line); |
| border-radius: 6px; |
| padding: 0 12px; |
| color: var(--text); |
| background: #fff; |
| font: inherit; |
| } |
| |
| button { |
| height: 42px; |
| border: 0; |
| border-radius: 6px; |
| padding: 0 14px; |
| color: #fff; |
| background: var(--brand); |
| cursor: pointer; |
| font: 700 14px Arial, Helvetica, sans-serif; |
| white-space: nowrap; |
| } |
| |
| button:hover { |
| background: var(--brand-dark); |
| } |
| |
| button.secondary { |
| color: var(--text); |
| background: #e8edf5; |
| } |
| |
| button.secondary:hover { |
| background: #dce4ef; |
| } |
| |
| button.danger { |
| background: var(--danger); |
| } |
| |
| .form { |
| display: grid; |
| grid-template-columns: 90px 1fr 160px 2fr auto; |
| gap: 12px; |
| padding: 16px; |
| border-bottom: 1px solid var(--line); |
| } |
| |
| .table-wrap { |
| overflow-x: auto; |
| } |
| |
| table { |
| width: 100%; |
| border-collapse: collapse; |
| min-width: 760px; |
| } |
| |
| th, |
| td { |
| padding: 13px 16px; |
| border-bottom: 1px solid var(--line); |
| text-align: left; |
| vertical-align: top; |
| } |
| |
| th { |
| color: var(--muted); |
| background: #f8fafc; |
| font-size: 12px; |
| text-transform: uppercase; |
| } |
| |
| tr:last-child td { |
| border-bottom: 0; |
| } |
| |
| .badge { |
| display: inline-flex; |
| min-width: 82px; |
| justify-content: center; |
| padding: 5px 8px; |
| border-radius: 999px; |
| background: #eef6f4; |
| color: #0f5f59; |
| font-size: 12px; |
| font-weight: 700; |
| text-transform: capitalize; |
| } |
| |
| .empty { |
| padding: 36px 16px; |
| color: var(--muted); |
| text-align: center; |
| } |
| |
| @media (max-width: 800px) { |
| header, |
| .toolbar, |
| .form { |
| grid-template-columns: 1fr; |
| } |
| |
| header { |
| display: block; |
| } |
| |
| .stats { |
| justify-content: flex-start; |
| margin-top: 16px; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <main> |
| <header> |
| <div> |
| <h1>Simple 10 Database</h1> |
| <p>Browser database page using the 10 Hugging Face dataset records.</p> |
| </div> |
| <div class="stats"> |
| <div class="stat"> |
| <strong id="totalCount">0</strong> |
| <span>Total</span> |
| </div> |
| <div class="stat"> |
| <strong id="visibleCount">0</strong> |
| <span>Visible</span> |
| </div> |
| </div> |
| </header> |
|
|
| <section class="panel" aria-label="Database records"> |
| <div class="toolbar"> |
| <input id="searchInput" type="search" placeholder="Search records"> |
| <select id="categoryFilter" aria-label="Filter by category"> |
| <option value="all">All categories</option> |
| <option value="starter">Starter</option> |
| <option value="standard">Standard</option> |
| <option value="advanced">Advanced</option> |
| </select> |
| <button class="secondary" id="resetButton" type="button">Reset Data</button> |
| </div> |
|
|
| <form class="form" id="recordForm"> |
| <input id="recordId" type="number" min="1" placeholder="ID" required> |
| <input id="recordName" type="text" placeholder="Name" required> |
| <select id="recordCategory" required> |
| <option value="starter">Starter</option> |
| <option value="standard">Standard</option> |
| <option value="advanced">Advanced</option> |
| </select> |
| <input id="recordDescription" type="text" placeholder="Description" required> |
| <button type="submit">Add Record</button> |
| </form> |
|
|
| <div class="table-wrap"> |
| <table> |
| <thead> |
| <tr> |
| <th>ID</th> |
| <th>Name</th> |
| <th>Category</th> |
| <th>Description</th> |
| <th>Action</th> |
| </tr> |
| </thead> |
| <tbody id="recordTable"></tbody> |
| </table> |
| <div class="empty" id="emptyState" hidden>No records found.</div> |
| </div> |
| </section> |
| </main> |
|
|
| <script> |
| const seedRecords = [ |
| { id: 1, name: "Alpha", category: "starter", description: "First sample database record" }, |
| { id: 2, name: "Beta", category: "starter", description: "Second sample database record" }, |
| { id: 3, name: "Gamma", category: "starter", description: "Third sample database record" }, |
| { id: 4, name: "Delta", category: "standard", description: "Fourth sample database record" }, |
| { id: 5, name: "Epsilon", category: "standard", description: "Fifth sample database record" }, |
| { id: 6, name: "Zeta", category: "standard", description: "Sixth sample database record" }, |
| { id: 7, name: "Eta", category: "advanced", description: "Seventh sample database record" }, |
| { id: 8, name: "Theta", category: "advanced", description: "Eighth sample database record" }, |
| { id: 9, name: "Iota", category: "advanced", description: "Ninth sample database record" }, |
| { id: 10, name: "Kappa", category: "advanced", description: "Tenth sample database record" } |
| ]; |
| |
| const storageKey = "simple-10-database-records"; |
| const table = document.getElementById("recordTable"); |
| const emptyState = document.getElementById("emptyState"); |
| const totalCount = document.getElementById("totalCount"); |
| const visibleCount = document.getElementById("visibleCount"); |
| const searchInput = document.getElementById("searchInput"); |
| const categoryFilter = document.getElementById("categoryFilter"); |
| const form = document.getElementById("recordForm"); |
| |
| let records = loadRecords(); |
| |
| function loadRecords() { |
| const stored = localStorage.getItem(storageKey); |
| return stored ? JSON.parse(stored) : [...seedRecords]; |
| } |
| |
| function saveRecords() { |
| localStorage.setItem(storageKey, JSON.stringify(records)); |
| } |
| |
| function getFilteredRecords() { |
| const query = searchInput.value.trim().toLowerCase(); |
| const category = categoryFilter.value; |
| |
| return records |
| .filter((record) => category === "all" || record.category === category) |
| .filter((record) => { |
| const text = `${record.id} ${record.name} ${record.category} ${record.description}`.toLowerCase(); |
| return text.includes(query); |
| }) |
| .sort((a, b) => a.id - b.id); |
| } |
| |
| function render() { |
| const filtered = getFilteredRecords(); |
| table.innerHTML = ""; |
| |
| for (const record of filtered) { |
| const row = document.createElement("tr"); |
| row.innerHTML = ` |
| <td>${record.id}</td> |
| <td>${escapeHtml(record.name)}</td> |
| <td><span class="badge">${escapeHtml(record.category)}</span></td> |
| <td>${escapeHtml(record.description)}</td> |
| <td><button class="danger" type="button" data-id="${record.id}">Delete</button></td> |
| `; |
| table.appendChild(row); |
| } |
| |
| totalCount.textContent = String(records.length); |
| visibleCount.textContent = String(filtered.length); |
| emptyState.hidden = filtered.length > 0; |
| } |
| |
| function escapeHtml(value) { |
| return String(value) |
| .replaceAll("&", "&") |
| .replaceAll("<", "<") |
| .replaceAll(">", ">") |
| .replaceAll('"', """) |
| .replaceAll("'", "'"); |
| } |
| |
| form.addEventListener("submit", (event) => { |
| event.preventDefault(); |
| |
| const id = Number(document.getElementById("recordId").value); |
| if (records.some((record) => record.id === id)) { |
| alert("This ID already exists."); |
| return; |
| } |
| |
| records.push({ |
| id, |
| name: document.getElementById("recordName").value.trim(), |
| category: document.getElementById("recordCategory").value, |
| description: document.getElementById("recordDescription").value.trim() |
| }); |
| |
| saveRecords(); |
| form.reset(); |
| render(); |
| }); |
| |
| table.addEventListener("click", (event) => { |
| const button = event.target.closest("button[data-id]"); |
| if (!button) { |
| return; |
| } |
| |
| const id = Number(button.dataset.id); |
| records = records.filter((record) => record.id !== id); |
| saveRecords(); |
| render(); |
| }); |
| |
| document.getElementById("resetButton").addEventListener("click", () => { |
| records = [...seedRecords]; |
| saveRecords(); |
| render(); |
| }); |
| |
| searchInput.addEventListener("input", render); |
| categoryFilter.addEventListener("change", render); |
| render(); |
| </script> |
| </body> |
| </html> |
|
|