Your IP : 216.73.217.78


Current Path : /home/seto/indexator.pm/templates/projects/
Upload File :
Current File : /home/seto/indexator.pm/templates/projects/create.php

<?php
$title = 'New Project — Indexator';
ob_start();
?>
<div class="row justify-content-center">
    <div class="col-lg-7">
        <h1 class="h3 mb-4">New Project</h1>

        <!-- Top-level mode tabs -->
        <ul class="nav nav-tabs mb-4" id="modeTabs" role="tablist">
            <li class="nav-item">
                <button class="nav-link active" id="mode-single" data-bs-toggle="tab"
                        data-bs-target="#panel-single" type="button">Single project</button>
            </li>
            <li class="nav-item">
                <button class="nav-link" id="mode-bulk" data-bs-toggle="tab"
                        data-bs-target="#panel-bulk" type="button">Bulk import</button>
            </li>
        </ul>

        <div class="tab-content">

            <!-- ── Single project ──────────────────────────────────────────── -->
            <div class="tab-pane fade show active" id="panel-single">
                <form method="POST" action="/projects" enctype="multipart/form-data">
                    <input type="hidden" name="_csrf" value="<?= htmlspecialchars($csrf) ?>">

                    <div class="mb-3">
                        <label for="name" class="form-label fw-semibold">Project name</label>
                        <input type="text" id="name" name="name"
                               class="form-control <?= isset($errors['name']) ? 'is-invalid' : '' ?>"
                               value="<?= htmlspecialchars($old['name'] ?? '') ?>" required maxlength="255">
                        <?php if (isset($errors['name'])): ?>
                            <div class="invalid-feedback"><?= htmlspecialchars($errors['name']) ?></div>
                        <?php endif; ?>
                    </div>

                    <div class="mb-3">
                        <label for="domain" class="form-label fw-semibold">Domain</label>
                        <input type="text" id="domain" name="domain"
                               class="form-control <?= isset($errors['domain']) ? 'is-invalid' : '' ?>"
                               value="<?= htmlspecialchars($old['domain'] ?? '') ?>"
                               placeholder="example.com" required maxlength="255">
                        <?php if (isset($errors['domain'])): ?>
                            <div class="invalid-feedback"><?= htmlspecialchars($errors['domain']) ?></div>
                        <?php endif; ?>
                    </div>

                    <!-- Import mode -->
                    <div class="mb-3">
                        <label class="form-label fw-semibold">Import source</label>
                        <ul class="nav nav-pills mb-3" id="importTabs" role="tablist">
                            <li class="nav-item">
                                <button class="nav-link active" id="tab-manual" data-bs-toggle="pill"
                                        data-bs-target="#panel-manual" type="button">Manual list</button>
                            </li>
                            <li class="nav-item">
                                <button class="nav-link" id="tab-file" data-bs-toggle="pill"
                                        data-bs-target="#panel-file" type="button">File (TXT/CSV)</button>
                            </li>
                            <li class="nav-item">
                                <button class="nav-link" id="tab-sitemap" data-bs-toggle="pill"
                                        data-bs-target="#panel-sitemap" type="button">Sitemap URL</button>
                            </li>
                        </ul>

                        <input type="hidden" name="source_type" id="source_type"
                               value="<?= htmlspecialchars($old['source_type'] ?? 'manual') ?>">

                        <div class="tab-content border rounded p-3">
                            <div class="tab-pane fade show active" id="panel-manual">
                                <label for="urls_manual" class="form-label text-muted small">One URL per line</label>
                                <textarea id="urls_manual" name="urls_manual"
                                          class="form-control font-monospace" rows="10"
                                          placeholder="https://example.com/page-1&#10;https://example.com/page-2"><?= htmlspecialchars($old['urls_manual'] ?? '') ?></textarea>
                            </div>
                            <div class="tab-pane fade" id="panel-file">
                                <label for="urls_file" class="form-label text-muted small">TXT or CSV file (one URL per line / first column)</label>
                                <input type="file" id="urls_file" name="urls_file" class="form-control"
                                       accept=".txt,.csv,text/plain,text/csv">
                            </div>
                            <div class="tab-pane fade" id="panel-sitemap">
                                <label for="sitemap_url" class="form-label text-muted small">XML sitemap URL</label>
                                <input type="url" id="sitemap_url" name="sitemap_url" class="form-control"
                                       value="<?= htmlspecialchars($old['sitemap_url'] ?? '') ?>"
                                       placeholder="https://example.com/sitemap.xml">
                                <?php if (isset($errors['sitemap'])): ?>
                                    <div class="text-danger small mt-1"><?= htmlspecialchars($errors['sitemap']) ?></div>
                                <?php endif; ?>
                            </div>
                        </div>
                    </div>

                    <div class="d-flex gap-2">
                        <button type="submit" class="btn btn-dark">Create Project</button>
                        <a href="/projects" class="btn btn-outline-secondary">Cancel</a>
                    </div>
                </form>
            </div>

            <!-- ── Bulk import ─────────────────────────────────────────────── -->
            <div class="tab-pane fade" id="panel-bulk">
                <div id="bulk-form">
                    <div class="mb-3">
                        <label for="bulk-domains" class="form-label fw-semibold">Domains</label>
                        <textarea id="bulk-domains" class="form-control font-monospace" rows="12"
                                  placeholder="example.com&#10;another-site.com&#10;third-site.net"></textarea>
                        <div class="form-text">One domain per line. http/https prefix is stripped automatically.</div>
                    </div>

                    <div class="mb-4">
                        <label for="bulk-sitemap-path" class="form-label fw-semibold">Sitemap path</label>
                        <input type="text" id="bulk-sitemap-path" class="form-control"
                               value="/sitemap.xml" placeholder="/sitemap.xml" maxlength="255">
                        <div class="form-text">Applied to all domains: <code>https://domain.com<span id="sitemap-path-preview">/sitemap.xml</span></code></div>
                    </div>

                    <div class="d-flex gap-2">
                        <button type="button" id="bulk-start-btn" class="btn btn-dark">Start Import</button>
                        <a href="/projects" class="btn btn-outline-secondary">Cancel</a>
                    </div>
                </div>

                <!-- Progress area (hidden until import starts) -->
                <div id="bulk-progress" class="mt-4" style="display:none;">
                    <div class="d-flex justify-content-between align-items-center mb-2">
                        <span class="fw-semibold">Import progress</span>
                        <span id="bulk-counter" class="text-muted small"></span>
                    </div>
                    <div class="progress mb-3" style="height:8px;">
                        <div id="bulk-progress-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-dark"
                             style="width:0%"></div>
                    </div>

                    <!-- Live log -->
                    <div id="bulk-log-wrap" class="border rounded bg-dark text-white font-monospace p-3"
                         style="max-height:320px; overflow-y:auto; font-size:.8rem;">
                    </div>

                    <!-- Summary (shown on done) -->
                    <div id="bulk-summary" class="mt-3" style="display:none;"></div>
                </div>
            </div>

        </div><!-- /tab-content -->
    </div>
</div>

<script>
document.addEventListener('DOMContentLoaded', function () {
    // Sync single-project import tabs
    const tabMap = { 'tab-manual': 'manual', 'tab-file': 'file', 'tab-sitemap': 'sitemap' };
    document.querySelectorAll('[data-bs-toggle="pill"]').forEach(function (btn) {
        btn.addEventListener('shown.bs.tab', function () {
            document.getElementById('source_type').value = tabMap[btn.id] || 'manual';
        });
    });
    const sourceType = '<?= htmlspecialchars($old['source_type'] ?? 'manual') ?>';
    const tabId = Object.entries(tabMap).find(([, v]) => v === sourceType)?.[0];
    if (tabId && tabId !== 'tab-manual') {
        const tabEl = document.getElementById(tabId);
        if (tabEl) new bootstrap.Tab(tabEl).show();
    }

    // Sitemap path preview
    const pathInput = document.getElementById('bulk-sitemap-path');
    const pathPreview = document.getElementById('sitemap-path-preview');
    if (pathInput && pathPreview) {
        pathInput.addEventListener('input', function () {
            pathPreview.textContent = pathInput.value || '/sitemap.xml';
        });
    }

    // ── Bulk import ────────────────────────────────────────────────────────
    const startBtn      = document.getElementById('bulk-start-btn');
    const bulkForm      = document.getElementById('bulk-form');
    const progressArea  = document.getElementById('bulk-progress');
    const progressBar   = document.getElementById('bulk-progress-bar');
    const counterEl     = document.getElementById('bulk-counter');
    const logWrap       = document.getElementById('bulk-log-wrap');
    const summaryEl     = document.getElementById('bulk-summary');

    if (!startBtn) return;

    startBtn.addEventListener('click', function () {
        const domains = document.getElementById('bulk-domains').value.trim();
        const sitemapPath = pathInput.value.trim() || '/sitemap.xml';

        if (!domains) {
            alert('Please enter at least one domain.');
            return;
        }

        const csrf = document.querySelector('meta[name="csrf-token"]').content;

        startBtn.disabled    = true;
        startBtn.textContent = 'Starting…';

        fetch('/api/bulk-import/start', {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: '_csrf=' + encodeURIComponent(csrf)
                + '&domains=' + encodeURIComponent(domains)
                + '&sitemap_path=' + encodeURIComponent(sitemapPath),
        })
        .then(function (res) { return res.json(); })
        .then(function (data) {
            if (data.error) { alert(data.error); startBtn.disabled = false; startBtn.textContent = 'Start Import'; return; }

            bulkForm.style.display  = 'none';
            progressArea.style.display = '';
            pollBulkJob(data.job_id, data.total);
        })
        .catch(function () {
            alert('Failed to start import.');
            startBtn.disabled    = false;
            startBtn.textContent = 'Start Import';
        });
    });

    function pollBulkJob(jobId, total) {
        let lastRowCount = 0;
        let done = false;

        const timer = setInterval(function () {
            if (done) { clearInterval(timer); return; }

            fetch('/api/bulk-import/' + jobId + '/status')
                .then(function (res) { return res.json(); })
                .then(function (data) {
                    const rows = data.rows || [];

                    // Append new log lines
                    for (let i = lastRowCount; i < rows.length; i++) {
                        const r   = rows[i];
                        const div = document.createElement('div');
                        div.className = r.type === 'OK' ? 'text-success' : r.type === 'ERROR' ? 'text-danger' : 'text-secondary';
                        div.textContent = (r.type === 'OK' ? '✓' : r.type === 'ERROR' ? '✗' : '—')
                            + '  ' + r.domain + '  ' + r.detail;
                        logWrap.appendChild(div);
                    }
                    lastRowCount = rows.length;

                    // Auto-scroll
                    logWrap.scrollTop = logWrap.scrollHeight;

                    // Progress bar
                    if (total > 0) {
                        const pct = Math.round(rows.length / total * 100);
                        progressBar.style.width = pct + '%';
                        counterEl.textContent   = rows.length + ' / ' + total;
                    }

                    if (data.status === 'done' || data.status === 'error') {
                        done = true;
                        clearInterval(timer);
                        progressBar.classList.remove('progress-bar-animated');

                        if (data.summary) {
                            const s = data.summary;
                            summaryEl.style.display = '';
                            summaryEl.innerHTML =
                                '<div class="alert alert-success mb-2">'
                                + '<strong>Import complete.</strong> '
                                + 'Created: <strong>' + s.created + '</strong> &nbsp;'
                                + 'Skipped: <strong>' + s.skipped + '</strong> &nbsp;'
                                + 'Errors: <strong>' + s.errors + '</strong>'
                                + '</div>'
                                + '<a href="/projects" class="btn btn-dark">View Projects</a>';
                        }
                    }
                })
                .catch(function () { /* ignore transient errors */ });
        }, 1500);
    }
});
</script>
<?php
$content = ob_get_clean();
require __DIR__ . '/../layout.php';