/**
* Join UI — page for joining a group from another server via invite
*/
import { renderPage } from './page-layout.js'
const PAGE_CSS = `
h1 { font-size: 28px; margin-bottom: 10px; }
.subtitle { color: #8b949e; margin-bottom: 30px; }
.card h2 { font-size: 18px; margin-bottom: 16px; }
.form-input, .form-textarea {
width: 100%; padding: 10px 14px; background: #0d1117;
border: 1px solid #30363d; border-radius: 6px;
color: #c9d1d9; font-size: 14px; font-family: monospace;
}
.form-input:focus, .form-textarea:focus { outline: none; border-color: #58a6ff; }
.form-textarea { min-height: 80px; resize: vertical; }
.step { display: none; }
.step.active { display: block; }
.group-preview {
background: #0d1117; border: 1px solid #30363d;
border-radius: 6px; padding: 16px; margin-bottom: 16px;
}
.group-preview h3 { font-size: 16px; margin-bottom: 8px; color: #58a6ff; }
.group-preview p { font-size: 14px; color: #8b949e; }
.repo-checkbox {
display: flex; align-items: center; gap: 10px;
padding: 10px 14px; background: #0d1117; border: 1px solid #30363d;
border-radius: 6px; margin-bottom: 8px; cursor: pointer;
}
.repo-checkbox:hover { border-color: #58a6ff; }
.repo-checkbox input[type="checkbox"] { width: 16px; height: 16px; accent-color: #238636; }
.repo-checkbox label { flex: 1; cursor: pointer; font-size: 14px; }
.success-box {
background: #0d1117; border: 1px solid #238636;
border-radius: 6px; padding: 20px; text-align: center;
}
.success-box h3 { color: #238636; font-size: 20px; margin-bottom: 10px; }
.toast {
position: fixed; bottom: 20px; right: 20px;
background: #238636; color: white; padding: 12px 20px;
border-radius: 6px; font-size: 14px; z-index: 2000;
opacity: 0; transform: translateY(10px);
transition: opacity 0.3s, transform 0.3s;
}
.toast.visible { opacity: 1; transform: translateY(0); }
.toast.error { background: #da3633; }
`
export function renderJoinPage (pathPrefix = '') {
const content = `
<h1>Join Group</h1>
<div class="subtitle">Connect to a group on another server using an invite code</div>
<div class="step active" id="step-invite">
<div class="card">
<h2>Step 1: Enter Invite Code</h2>
<div class="form-group">
<label for="invite-hex">Invite Hex</label>
<textarea class="form-textarea" id="invite-hex" placeholder="Paste the invite hex from the other server..."></textarea>
</div>
<button onclick="connectWithInvite()" id="connect-btn">Connect</button>
</div>
</div>
<div class="step" id="step-preview">
<div class="card">
<h2>Step 2: Select Repos to Sync</h2>
<div id="group-preview"></div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
<label style="font-size:14px;">Repositories</label>
<label style="font-size:12px;color:#8b949e;cursor:pointer;" onclick="toggleAllRepos()">
<input type="checkbox" id="select-all" checked> Select All
</label>
</div>
<div id="repo-list"></div>
<div style="display:flex;gap:10px;margin-top:16px;">
<button onclick="confirmJoin()" id="confirm-btn">Confirm Join</button>
<button class="secondary" onclick="goBack()">Back</button>
</div>
</div>
</div>
<div class="step" id="step-success">
<div class="card">
<div class="success-box">
<h3>Joined!</h3>
<p style="color:#8b949e;margin-bottom:16px;" id="success-msg">You have joined the group.</p>
<a href="${pathPrefix}/groups" style="text-decoration:none;"><button>View Groups</button></a>
</div>
</div>
</div>
<div id="toast" class="toast"></div>
`
const scripts = `
function getBasePath() { return '${pathPrefix}'; }
var joinResult = null;
async function connectWithInvite() {
var invite = document.getElementById('invite-hex').value.trim();
if (!invite) { showToast('Paste an invite code first', true); return; }
var btn = document.getElementById('connect-btn');
btn.disabled = true; btn.textContent = 'Connecting...';
try {
var res = await window.fetch(getBasePath() + '/api/groups/join', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ invite: invite }) });
var data = await res.json();
if (!res.ok) throw new Error(data.error || 'Failed to connect');
joinResult = data;
var preview = document.getElementById('group-preview');
if (data.group) {
preview.innerHTML = '<div class="group-preview"><h3>' + esc(data.group.displayName || data.group.name || data.groupName) + '</h3>' + (data.group.description ? '<p>' + esc(data.group.description) + '</p>' : '') + '<p style="margin-top:8px;">' + (data.group.repos ? data.group.repos.length : 0) + ' repos, ' + (data.group.members ? data.group.members.length : 0) + ' members</p></div>';
var repoList = document.getElementById('repo-list');
if (data.group.repos && data.group.repos.length > 0) {
repoList.innerHTML = data.group.repos.map(function(name) { return '<div class="repo-checkbox"><input type="checkbox" id="repo-' + name + '" value="' + esc(name) + '" checked><label for="repo-' + name + '">' + esc(name) + '</label></div>'; }).join('');
} else { repoList.innerHTML = '<p style="color:#8b949e;">No repos in this group yet</p>'; }
} else {
preview.innerHTML = '<div class="group-preview"><h3>Registry Connected</h3><p>Connected to shared registry. Group data will sync shortly.</p></div>';
document.getElementById('repo-list').innerHTML = '<p style="color:#8b949e;">Syncing...</p>';
}
document.getElementById('step-invite').classList.remove('active');
document.getElementById('step-preview').classList.add('active');
} catch (err) { showToast(err.message, true); }
finally { btn.disabled = false; btn.textContent = 'Connect'; }
}
function toggleAllRepos() {
var checked = document.getElementById('select-all').checked;
document.querySelectorAll('#repo-list input[type="checkbox"]').forEach(function(cb) { cb.checked = checked; });
}
async function confirmJoin() {
var selectedRepos = [];
document.querySelectorAll('#repo-list input[type="checkbox"]:checked').forEach(function(cb) { selectedRepos.push(cb.value); });
var groupName = joinResult && joinResult.groupName;
if (groupName && selectedRepos.length > 0) {
var btn = document.getElementById('confirm-btn');
btn.disabled = true; btn.textContent = 'Saving...';
try {
var res = await window.fetch(getBasePath() + '/api/groups/' + encodeURIComponent(groupName) + '/sync', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ repos: selectedRepos }) });
if (!res.ok) { var err = await res.json(); throw new Error(err.error || 'Failed to save sync config'); }
} catch (err) { showToast(err.message, true); btn.disabled = false; btn.textContent = 'Confirm Join'; return; }
}
document.getElementById('success-msg').textContent = groupName ? 'Joined group "' + groupName + '" — syncing ' + selectedRepos.length + ' repo(s)' : 'Connected to shared registry';
document.getElementById('step-preview').classList.remove('active');
document.getElementById('step-success').classList.add('active');
}
function goBack() { document.getElementById('step-preview').classList.remove('active'); document.getElementById('step-invite').classList.add('active'); }
function esc(t) { if (!t) return ''; return String(t).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
function showToast(msg, isError) { var t = document.getElementById('toast'); t.textContent = msg; t.className = 'toast visible' + (isError ? ' error' : ''); setTimeout(function() { t.className = 'toast'; }, 3000); }
`
return renderPage('Join Group', content, { pathPrefix, activePage: 'groups', css: PAGE_CSS, scripts, maxWidth: 700 })
}