OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git-v3 / lib / web / components / org-tabs.js

61 lines · javascript ref:
/**
 * Shared tab chrome for per-org pages.
 *
 * Sibling to repo-tabs.js. Every `/orgs/:name/*` page renders the same
 * tab row at the top so navigation stays consistent across Overview /
 * Repos / Members / Issues / Settings.
 *
 * Style is identical to the repo tabs (#8b949e inactive, #c9d1d9 +
 * #58a6ff active-underline) so the two surfaces feel like siblings.
 */

import { escapeHtml } from '../helpers.js'

export const ORG_TAB_STYLES = `
<style>
  .org-tabs { display: flex; flex-wrap: wrap; gap: 0; align-items: flex-end; border-bottom: 1px solid #30363d; margin: 0 0 1rem; }
  .org-tabs a { padding: 0.55rem 0.9rem; color: #8b949e; text-decoration: none; border-bottom: 2px solid transparent; font-size: 0.85rem; margin-bottom: -1px; display: inline-flex; align-items: center; gap: 0.4rem; }
  .org-tabs a:hover { color: #c9d1d9; background: #161b22; }
  .org-tabs a.active { color: #c9d1d9; border-bottom-color: #58a6ff; font-weight: 500; background: transparent; }
  .org-tabs a .count { font-size: 0.7rem; color: #8b949e; background: #30363d; padding: 0.05rem 0.4rem; border-radius: 10px; }
  .org-tabs a.active .count { background: #1f6feb33; color: #c9d1d9; }
</style>`

const TABS = [
  { key: 'overview', label: 'Overview', path: '' },
  { key: 'repos',    label: 'Repos',    path: '/repos' },
  { key: 'members',  label: 'Members',  path: '/members' },
  { key: 'issues',   label: 'Issues',   path: '/issues' },
  { key: 'settings', label: 'Settings', path: '/settings', adminOnly: true }
]

/**
 * Render the tab row for an org page.
 *
 * @param {string} orgName
 * @param {string} activeTab — overview | repos | members | issues | settings
 * @param {object} [counts] — optional { issues, repos, members } badges
 * @param {string} [prefix] — daemon URL prefix (/git in production)
 * @param {boolean} [isAdmin] — when false, the Settings tab is hidden
 *   so non-admin viewers don't see a tab they can't action. (The
 *   Settings ROUTE itself is gated server-side; this is purely UX.)
 */
export function renderOrgTabs (orgName, activeTab, counts = {}, prefix = '', isAdmin = false) {
  const base = prefix + '/orgs/' + encodeURIComponent(orgName)
  const countFor = (key) => {
    if (key === 'issues' && typeof counts.issues === 'number') return `<span class="count">${counts.issues}</span>`
    if (key === 'repos' && typeof counts.repos === 'number') return `<span class="count">${counts.repos}</span>`
    if (key === 'members' && typeof counts.members === 'number') return `<span class="count">${counts.members}</span>`
    return ''
  }

  return `
<div class="org-tabs" role="tablist" aria-label="org navigation">
  ${TABS.filter(t => !t.adminOnly || isAdmin).map(t =>
    `<a href="${base}${t.path}"${t.key === activeTab ? ' class="active"' : ''} role="tab"${t.key === activeTab ? ' aria-selected="true"' : ''}>` +
    `${escapeHtml(t.label)}${countFor(t.key)}` +
    '</a>'
  ).join('')}
</div>`
}