o
    fe#j¨=  ã                
   @   sX  d Z ddlZddlZej dd¡ ej dd¡ ddlmZmZmZ ddl	m
Z
mZmZ e ¡ ZedƒZe e¡\ZZZer\edƒ erHeeƒ ed	ƒ erSed
ƒ nedƒ e d¡ edƒ er‹edƒ edeƒ › d�ƒ e ¡ Zer|eedƒƒ needƒƒ edƒ e d¡ e ¡ Zedeƒ › de
eƒ› dee ¡ p dƒ› deƒ › d�	ƒ dS )u!  Shared company todo list â€” tag/campaign model, mobile-first.

Group-by any axis (type/location/campaign/status), faceted filters, per-todo
activity log, campaign progress. Data model lives in todo.db (tag_types, tags,
todo_tags, todo_events, campaigns, items). See migrate_todo_tags.py.
é    Nz/opt/ngon/appsz/var/www/html/ngon)ÚAuthManagerÚgenerate_login_modal_htmlÚgenerate_access_denied_html)Úgenerate_dropdown_htmlÚgenerate_dropdown_cssÚgenerate_dropdown_jsÚtodozContent-Type: application/jsonÚ z6{"success": false, "error": "Authentication required"}z{"success": true}zContent-Type: text/html
z3<!DOCTYPE html><html><head><title>NGON Todo</title>z<style>z</style></head><body>z	Todo Listz</body></html>a·  <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>NGON Todo List</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
    <style>
        u…-  

        * { -webkit-tap-highlight-color: transparent; }
        /* kill the white autofill background browsers force on dark inputs */
        input:-webkit-autofill,
        input:-webkit-autofill:hover,
        input:-webkit-autofill:focus,
        textarea:-webkit-autofill,
        select:-webkit-autofill {
            -webkit-text-fill-color: #e0e0e0;
            -webkit-box-shadow: 0 0 0 1000px #0d0d0d inset;
            box-shadow: 0 0 0 1000px #0d0d0d inset;
            caret-color: #e0e0e0;
            transition: background-color 9999s ease-in-out 0s;
        }
        body {
            background: #1a1a1a;
            color: #e0e0e0;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            line-height: 1.4;
            margin: 0;
            padding: 0;
            padding-bottom: 90px; /* room for FAB */
            overflow-x: hidden;
        }

        /* Header â€” floating box style */
        .header {
            background-color: rgba(255,255,255,0.05);
            border: 1px solid rgba(255,255,255,0.1);
            border-radius: 8px;
            padding: 20px 24px;
            margin: 14px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 10px;
        }
        .header h1.dropdown-title {
            font-size: 1.6em;
            font-weight: 600;
            margin: 0;
            line-height: 1.2;
        }

        /* Sticky toolbar */
        .toolbar {
            position: sticky;
            top: 0;
            z-index: 50;
            background: #1a1a1a;
            padding: 8px 14px;
            display: flex;
            gap: 8px;
            align-items: center;
            flex-wrap: wrap;
            border-bottom: 1px solid #2a2a2a;
        }
        .toolbar .grow { flex: 1; min-width: 120px; }
        .tb-label { font-size: 12px; color: #888; }
        select.tb-select, .search-box {
            background: #0d0d0d;
            color: #e0e0e0;
            border: 1px solid #2a2a2a;
            border-radius: 8px;
            padding: 8px 10px;
            font-size: 14px;
            font-family: inherit;
        }
        select.tb-select:focus, .search-box:focus { outline: none; border-color: #4a9eff; }
        .search-box { width: 100%; box-sizing: border-box; }

        .btn {
            background: #2a2a2a;
            color: #e0e0e0;
            border: 1px solid #3a3a3a;
            padding: 8px 12px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 14px;
            transition: all 0.15s;
            white-space: nowrap;
        }
        .btn:hover { background: #3a3a3a; }
        .btn-primary { background: #00ff00; color: #000; border-color: #00ff00; font-weight: 600; }
        .btn-primary:hover { background: #00cc00; }
        .btn.active { background: #4a9eff; color: #000; border-color: #4a9eff; font-weight: 600; }
        .btn .badge {
            background: rgba(0,0,0,0.35); color: inherit; border-radius: 10px;
            padding: 0 6px; font-size: 11px; margin-left: 4px;
        }

        /* Active filter chips row */
        .active-filters { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 14px; margin: 8px 0; }
        .active-filters:empty { margin: 0; }
        .fchip {
            display: inline-flex; align-items: center; gap: 5px;
            background: #232a33; border: 1px solid #34465a; color: #cfe3ff;
            border-radius: 14px; padding: 3px 8px; font-size: 12px; cursor: pointer;
        }
        .fchip i { font-size: 10px; opacity: 0.7; }

        .main-content { padding: 0 14px 20px 14px; max-width: 1600px; margin: 0 auto; }

        /* Group section */
        .group { margin-top: 14px; }
        .group-header {
            display: flex; align-items: center; gap: 10px;
            padding: 8px 4px; cursor: pointer; user-select: none;
        }
        .group-caret { color: #666; font-size: 12px; width: 12px; transition: transform 0.15s; }
        .group.collapsed .group-caret { transform: rotate(-90deg); }
        .group-title { font-weight: 600; font-size: 15px; color: #e0e0e0; }
        .group-title .dot { display:inline-block; width:9px; height:9px; border-radius:50%; margin-right:7px; vertical-align: middle; }
        .group-count { font-size: 12px; color: #777; }
        .group.collapsed .group-body { display: none; }

        /* Campaign progress bar */
        .cprog { flex: 1; max-width: 220px; height: 7px; background: #2a2a2a; border-radius: 4px; overflow: hidden; }
        .cprog > span { display: block; height: 100%; background: #00ff00; }
        .cprog-text { font-size: 11px; color: #888; white-space: nowrap; }
        .cdue { font-size: 11px; color: #ffaa00; white-space: nowrap; }
        .cdue.overdue { color: #ff4444; }

        /* Todo cards */
        .todo-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 10px; align-items: start; }
        .todo-card {
            background: rgba(255,255,255,0.05);
            border: 1px solid rgba(255,255,255,0.1);
            border-left: 3px solid #888;
            border-radius: 10px;
            padding: 11px 12px;
            cursor: pointer;
            transition: background 0.12s;
        }
        .todo-card:hover { background: rgba(255,255,255,0.08); }
        .todo-card.s-open { border-left-color: #888; }
        .todo-card.s-in_progress { border-left-color: #4a9eff; }
        .todo-card.s-blocked { border-left-color: #ff4444; }
        .todo-card.s-done { border-left-color: #00ff00; opacity: 0.6; }
        .todo-card.s-done .todo-text { text-decoration: line-through; color: #999; }
        .todo-top { display: flex; align-items: flex-start; gap: 8px; }
        .todo-text { flex: 1; font-size: 14px; word-break: break-word; }
        .todo-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
        .chip {
            font-size: 11px; padding: 2px 7px; border-radius: 10px;
            border: 1px solid; background: transparent; white-space: nowrap;
        }
        .chip-campaign { font-weight: 600; }
        .todo-foot { display: flex; align-items: center; gap: 10px; margin-top: 8px; font-size: 11px; color: #666; }
        .todo-foot .note-badge { color: #888; }

        .status-pill {
            font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .3px;
            padding: 2px 7px; border-radius: 6px; flex-shrink: 0;
        }
        .pill-open { background:#333; color:#bbb; }
        .pill-in_progress { background:#13314d; color:#7cc0ff; }
        .pill-blocked { background:#4d1717; color:#ff8888; }
        .pill-done { background:#143d14; color:#7dff7d; }

        .empty-state { text-align: center; color: #666; padding: 50px 20px; font-size: 14px; }

        /* FAB */
        .fab {
            position: fixed; right: 18px; bottom: 18px; z-index: 60;
            background: #00ff00; color: #000; border: none;
            width: 56px; height: 56px; border-radius: 50%;
            font-size: 24px; cursor: pointer; box-shadow: 0 4px 14px rgba(0,0,0,0.5);
        }

        /* Bottom sheets */
        .sheet-overlay {
            display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.6);
            z-index: 100; align-items: flex-end; justify-content: center;
        }
        .sheet-overlay.active { display: flex; }
        .sheet {
            background: #1c1c1c; border-top: 1px solid #333;
            border-radius: 16px 16px 0 0; width: 100%; max-width: 640px;
            max-height: 88vh; overflow-y: auto; padding: 16px 16px 28px 16px;
            box-sizing: border-box; animation: slideUp 0.18s ease-out;
        }
        @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
        .sheet-grip { width: 40px; height: 4px; background: #444; border-radius: 2px; margin: 0 auto 12px auto; }
        .sheet h3 { margin: 0 0 14px 0; color: #00ff00; font-size: 18px; }
        .sheet-section { margin-bottom: 16px; }
        .sheet-section > label { display: block; font-size: 12px; color: #888; margin-bottom: 6px; text-transform: uppercase; letter-spacing: .3px; }
        .sheet input[type=text], .sheet input[type=date], .sheet textarea, .sheet select {
            width: 100%; box-sizing: border-box; background: #0d0d0d; color: #e0e0e0;
            border: 1px solid #2a2a2a; border-radius: 8px; padding: 10px 12px;
            font-size: 15px; font-family: inherit;
        }
        .sheet textarea { resize: vertical; min-height: 60px; }
        .sheet input:focus, .sheet textarea:focus, .sheet select:focus { outline: none; border-color: #00ff00; }

        /* selectable chips inside sheets */
        .chip-row { display: flex; flex-wrap: wrap; gap: 6px; }
        .chip-sel {
            font-size: 13px; padding: 6px 11px; border-radius: 16px;
            border: 1px solid #3a3a3a; background: #232323; color: #ccc; cursor: pointer;
            user-select: none;
        }
        .chip-sel.on { background: #4a9eff; color: #000; border-color: #4a9eff; font-weight: 600; }
        .chip-sel.cat.on { background: #00ff00; border-color: #00ff00; }
        .chip-add { border-style: dashed; color: #888; background: transparent; }
        .chip-group-label { font-size: 11px; color:#666; width: 100%; margin: 6px 0 2px; }

        .status-choice { display:flex; gap:6px; flex-wrap: wrap; }
        .status-choice .chip-sel.on.st-open { background:#888; border-color:#888; }
        .status-choice .chip-sel.on.st-in_progress { background:#4a9eff; border-color:#4a9eff; }
        .status-choice .chip-sel.on.st-blocked { background:#ff4444; border-color:#ff4444; color:#fff; }
        .status-choice .chip-sel.on.st-done { background:#00ff00; border-color:#00ff00; }

        /* activity log */
        .events { display: flex; flex-direction: column; gap: 8px; }
        .event { font-size: 13px; padding: 8px 10px; background: #161616; border-radius: 8px; }
        .event.note { border-left: 2px solid #4a9eff; }
        .event.sys { color: #888; font-size: 12px; background: transparent; padding: 3px 10px; }
        .event-meta { font-size: 10px; color: #666; margin-top: 3px; }
        .event-del { float: right; color:#664; cursor: pointer; }
        .add-note-row { display: flex; gap: 6px; margin-top: 8px; }
        .add-note-row textarea { flex: 1; min-height: 40px; }

        .sheet-buttons { display: flex; justify-content: space-between; gap: 8px; margin-top: 18px; }
        .sheet-buttons .right { display: flex; gap: 8px; }
        .btn-danger { background: transparent; color: #ff6666; border-color: #553333; }
        .btn-danger:hover { background: #3a1a1a; }

        /* page-changed banner */
        .changed-banner {
            display: none; position: fixed; top: 12px; left: 50%; transform: translateX(-50%);
            background: #ff6600; color: #000; padding: 10px 20px; border-radius: 8px;
            font-weight: 600; cursor: pointer; z-index: 200; box-shadow: 0 4px 12px rgba(0,0,0,0.5);
        }
        .changed-banner.show { display: block; }

        @media (max-width: 768px) {
            .header { padding: 14px 16px; margin: 10px; }
            .header h1.dropdown-title { font-size: 1.3em; }
            .todo-list { grid-template-columns: 1fr; }
            .toolbar { position: sticky; }
        }
    </style>
</head>
<body>

<div class="header">
    <div class="dropdown">
        <h1 class="dropdown-title">NGON Mining - Todo</h1>
        <div class="dropdown-content">u  </div>
    </div>
    <div class="header-buttons">
        <button class="btn" onclick="openManageSheet()"><i class="fas fa-tags"></i> Tags</button>
        <button class="btn" onclick="openCampaignSheet()"><i class="fas fa-bullhorn"></i> Campaigns</button>
    </div>
</div>

<div class="toolbar">
    <span class="tb-label">Group</span>
    <select id="groupBy" class="tb-select" onchange="onGroupByChange(this.value)"></select>
    <button id="filterBtn" class="btn" onclick="openFilterSheet()"><i class="fas fa-filter"></i> Filter</button>
    <button id="doneBtn" class="btn" onclick="toggleShowDone()"><i class="fas fa-check-double"></i> Done</button>
    <div class="grow"><input id="searchBox" class="search-box" type="text" placeholder="Search..." oninput="onSearch(this.value)"></div>
</div>

<div id="activeFilters" class="active-filters"></div>

<div id="changedBanner" class="changed-banner" onclick="refresh()">Updated elsewhere â€” tap to reload</div>

<div class="main-content">
    <div id="groupsContainer"></div>
    <div id="emptyState" class="empty-state" style="display:none;">Nothing here. Tap + to add a todo.</div>
</div>

<button class="fab" onclick="openCreateSheet()" title="New todo"><i class="fas fa-plus"></i></button>

<!-- Sheets (filled by JS) -->
<div id="detailSheet" class="sheet-overlay"><div class="sheet" id="detailSheetBody"></div></div>
<div id="createSheet" class="sheet-overlay"><div class="sheet" id="createSheetBody"></div></div>
<div id="filterSheet" class="sheet-overlay"><div class="sheet" id="filterSheetBody"></div></div>
<div id="campaignSheet" class="sheet-overlay"><div class="sheet" id="campaignSheetBody"></div></div>
<div id="manageSheet" class="sheet-overlay"><div class="sheet" id="manageSheetBody"></div></div>

<script>
    window.CURRENT_USER = Úunknownz:;
</script>
<script src="/todo/todo.js"></script>
<script>z</script>
</body>
</html>)Ú__doc__ÚcgiÚsysÚpathÚinsertÚmanagers.auth_managerr   r   r   Úlinksr   r   r   ÚFieldStorageÚformÚauthÚrequire_authÚauth_requiredÚshould_exitÚheadersÚprintÚexitÚis_authenticatedÚusernameÚget_user_accessÚ_user_accessÚreprÚget_current_user© r!   r!   úindex.pyÚ<module>   sf   


	÷   � �ú  ) � �×  , � �Ô