← back to centy-io__centy-app

Function bodies 471 total

All specs Real LLM only Function bodies
GlobalError function · typescript · L7-L27 (21 LOC)
app/global-error.tsx
export default function GlobalError({
  error,
}: {
  error: Error & { digest?: string }
}) {
  useEffect(() => {
    captureException(error)
  }, [error])

  return (
    <html lang="en" className="global-error-html">
      <body className="global-error-body">
        {/* `NextError` is the default Next.js error page component. Its type
        definition requires a `statusCode` prop. However, since the App Router
        does not expose status codes for errors, we simply pass 0 to render a
        generic error message. */}
        <NextError statusCode={0} />
      </body>
    </html>
  )
}
RootLayout function · typescript · L51-L89 (39 LOC)
app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html
      lang="en"
      suppressHydrationWarning
      className={`root-html ${notoSans.variable}`}
    >
      <head className="root-head">
        <Script src={GOOGLE_ANALYTICS_URL} strategy="afterInteractive" />
        <Script id="google-analytics" strategy="afterInteractive">
          {`
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', 'G-ZV5SD70Z2D');
          `}
        </Script>
      </head>
      <body suppressHydrationWarning className="root-body">
        <Providers>
          <DaemonDisconnectedOverlay />
          <DemoModeIndicator />
          <LegacyUrlRedirect />
          <MobileNotSupportedOverlay />
          <div className="app">
            <Header />
            <main className="app-main">
              <ClientRouteHandler>{children}
generateStaticParams function · typescript · L3-L11 (9 LOC)
app/[organization]/[project]/issues/[issueId]/page.tsx
export async function generateStaticParams() {
  return [
    {
      organization: '_placeholder',
      project: '_placeholder',
      issueId: '_placeholder',
    },
  ]
}
IssueDetailPage function · typescript · L13-L20 (8 LOC)
app/[organization]/[project]/issues/[issueId]/page.tsx
export default async function IssueDetailPage({
  params,
}: {
  params: Promise<{ issueId: string }>
}) {
  const { issueId } = await params
  return <IssueDetail issueNumber={issueId} />
}
ProjectLayout function · typescript · L5-L11 (7 LOC)
app/[organization]/[project]/layout.tsx
export default function ProjectLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <PathContextProvider>{children}</PathContextProvider>
}
generateStaticParams function · typescript · L3-L11 (9 LOC)
app/[organization]/[project]/users/[userId]/page.tsx
export async function generateStaticParams() {
  return [
    {
      organization: '_placeholder',
      project: '_placeholder',
      userId: '_placeholder',
    },
  ]
}
UserDetailPage function · typescript · L13-L20 (8 LOC)
app/[organization]/[project]/users/[userId]/page.tsx
export default async function UserDetailPage({
  params,
}: {
  params: Promise<{ userId: string }>
}) {
  const { userId } = await params
  return <UserDetail userId={userId} />
}
Repobility analyzer · published findings · https://repobility.com
OrgIssueDetailPage function · typescript · L7-L14 (8 LOC)
app/organizations/[orgSlug]/issues/[issueId]/page.tsx
export default async function OrgIssueDetailPage({
  params,
}: {
  params: Promise<{ orgSlug: string; issueId: string }>
}) {
  const { orgSlug, issueId } = await params
  return <OrgIssueDetail orgSlug={orgSlug} issueId={issueId} />
}
NewOrgIssuePage function · typescript · L7-L14 (8 LOC)
app/organizations/[orgSlug]/issues/new/page.tsx
export default async function NewOrgIssuePage({
  params,
}: {
  params: Promise<{ orgSlug: string }>
}) {
  const { orgSlug } = await params
  return <CreateOrgIssue orgSlug={orgSlug} />
}
OrgIssuesPage function · typescript · L7-L14 (8 LOC)
app/organizations/[orgSlug]/issues/page.tsx
export default async function OrgIssuesPage({
  params,
}: {
  params: Promise<{ orgSlug: string }>
}) {
  const { orgSlug } = await params
  return <OrgIssuesList orgSlug={orgSlug} />
}
OrganizationDetailPage function · typescript · L7-L14 (8 LOC)
app/organizations/[orgSlug]/page.tsx
export default async function OrganizationDetailPage({
  params,
}: {
  params: Promise<{ orgSlug: string }>
}) {
  const { orgSlug } = await params
  return <OrganizationDetail orgSlug={orgSlug} />
}
AssetGrid function · typescript · L17-L49 (33 LOC)
components/assets/AssetUploader/AssetGrid.tsx
export function AssetGrid({
  assets,
  pendingAssets,
  projectPath,
  issueId,
  onRemoveAsset,
  onRemovePending,
}: AssetGridProps) {
  if (assets.length === 0 && pendingAssets.length === 0) {
    return null
  }

  return (
    <div className="asset-grid">
      {assets.map(asset => (
        <AssetPreviewItem
          key={asset.filename}
          asset={asset}
          projectPath={projectPath}
          issueId={issueId!}
          onRemove={() => onRemoveAsset(asset.filename)}
        />
      ))}
      {pendingAssets.map(pending => (
        <PendingAssetPreviewItem
          key={pending.id}
          pending={pending}
          onRemove={() => onRemovePending(pending.id)}
        />
      ))}
    </div>
  )
}
AssetPreviewContent function · typescript · L13-L39 (27 LOC)
components/assets/AssetUploader/AssetPreviewItem.tsx
function AssetPreviewContent({
  loading,
  type,
  previewUrl,
  filename,
}: {
  loading: boolean
  type: string
  previewUrl: string | null
  filename: string
}) {
  if (loading) return <div className="asset-loading">Loading...</div>
  if (type === 'image' && previewUrl) {
    return (
      <img src={previewUrl} alt={filename} className="asset-preview-image" />
    )
  }
  if (type === 'video' && previewUrl) {
    return <video src={previewUrl} className="asset-preview-video" muted />
  }
  return (
    <div className="asset-preview-pdf">
      <span className="asset-preview-pdf-icon">PDF</span>
      <span className="asset-preview-pdf-name">{filename}</span>
    </div>
  )
}
AssetPreviewItem function · typescript · L41-L72 (32 LOC)
components/assets/AssetUploader/AssetPreviewItem.tsx
export function AssetPreviewItem({
  asset,
  projectPath,
  issueId,
  onRemove,
}: AssetPreviewItemProps) {
  const { previewUrl, loading, type } = useAssetPreview(
    asset,
    projectPath,
    issueId
  )

  return (
    <div className="asset-preview">
      <AssetPreviewContent
        loading={loading}
        type={type}
        previewUrl={previewUrl}
        filename={asset.filename}
      />
      <div className="asset-overlay">
        <button
          className="asset-remove-btn"
          onClick={onRemove}
          title="Remove asset"
        >
          x
        </button>
      </div>
    </div>
  )
}
DropZone function · typescript · L10-L58 (49 LOC)
components/assets/AssetUploader/DropZone.tsx
export function DropZone({ fileInputRef, onFilesSelected }: DropZoneProps) {
  const [isDragging, setIsDragging] = useState(false)
  return (
    <div
      className={`drop-zone ${isDragging ? 'dragging' : ''}`}
      onDragOver={e => {
        e.preventDefault()
        setIsDragging(true)
      }}
      onDragLeave={e => {
        e.preventDefault()
        setIsDragging(false)
      }}
      onDrop={e => {
        e.preventDefault()
        setIsDragging(false)
        if (e.dataTransfer.files.length > 0) {
          onFilesSelected(e.dataTransfer.files)
        }
      }}
      onClick={() => {
        if (fileInputRef.current) fileInputRef.current.click()
      }}
    >
      <input
        className="drop-zone-input"
        ref={fileInputRef}
        type="file"
        multiple
        accept="image/png,image/jpeg,image/gif,image/webp,video/mp4,video/webm,application/pdf"
        onChange={e => {
          if (e.target.files) onFilesSelected(e.target.files)
        }}
        
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
PendingAssetPreviewItem function · typescript · L10-L53 (44 LOC)
components/assets/AssetUploader/PendingAssetPreviewItem.tsx
export function PendingAssetPreviewItem({
  pending,
  onRemove,
}: PendingAssetPreviewItemProps) {
  const type = pending.file.type.startsWith('image/')
    ? 'image'
    : pending.file.type.startsWith('video/')
      ? 'video'
      : 'pdf'

  return (
    <div className={`asset-preview pending ${pending.status}`}>
      {type === 'image' && pending.preview ? (
        <img
          src={pending.preview}
          alt={pending.file.name}
          className="asset-preview-image"
        />
      ) : type === 'video' ? (
        <div className="asset-preview-pdf">
          <span className="asset-preview-pdf-icon">VID</span>
          <span className="asset-preview-pdf-name">{pending.file.name}</span>
        </div>
      ) : (
        <div className="asset-preview-pdf">
          <span className="asset-preview-pdf-icon">PDF</span>
          <span className="asset-preview-pdf-name">{pending.file.name}</span>
        </div>
      )}

      {pending.status === 'error' && (
        <div
uploadAssetToServer function · typescript · L15-L44 (30 LOC)
components/assets/AssetUploader/uploadAsset.ts
export async function uploadAssetToServer(
  pending: PendingAsset,
  uploadTargetId: string,
  projectPath: string
): Promise<UploadResult> {
  try {
    const arrayBuffer = await pending.file.arrayBuffer()
    const request = create(AddAssetRequestSchema, {
      projectPath,
      issueId: uploadTargetId,
      filename: pending.file.name,
      data: new Uint8Array(arrayBuffer),
    })
    const response = await centyClient.addAsset(request)

    if (response.success && response.asset) {
      if (pending.preview) URL.revokeObjectURL(pending.preview)
      return { success: true, asset: response.asset }
    }
    return {
      success: false,
      error: response.error || 'Upload failed',
    }
  } catch (err) {
    return {
      success: false,
      error: err instanceof Error ? err.message : 'Upload failed',
    }
  }
}
useAssetHandlers function · typescript · L18-L77 (60 LOC)
components/assets/AssetUploader/useAssetHandlers.ts
export function useAssetHandlers({
  uploader,
  targetId,
  mode,
  onPendingChange,
  ref,
}: UseAssetHandlersOptions) {
  const handleFiles = useCallback(
    async (files: FileList | File[]) => {
      for (const file of Array.from(files)) {
        const validationError = uploader.validateFile(file)
        if (validationError) {
          uploader.setError(validationError)
          continue
        }
        const preview = file.type.startsWith('image/')
          ? URL.createObjectURL(file)
          : undefined
        const pending: PendingAsset = {
          id: crypto.randomUUID(),
          file,
          preview,
          status: mode === 'edit' && targetId ? 'uploading' : 'pending',
        }
        uploader.setPendingAssets(prev => {
          const updated = [...prev, pending]
          if (onPendingChange) onPendingChange(updated)
          return updated
        })
        if (mode === 'edit' && targetId) {
          await uploader.uploadAsset(pending, targetId)
 
useAssetPreview function · typescript · L8-L57 (50 LOC)
components/assets/AssetUploader/useAssetPreview.ts
export function useAssetPreview(
  asset: Asset,
  projectPath: string,
  issueId: string
) {
  const [previewUrl, setPreviewUrl] = useState<string | null>(null)
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    let mounted = true

    const loadPreview = async () => {
      if (
        asset.mimeType.startsWith('image/') ||
        asset.mimeType.startsWith('video/')
      ) {
        try {
          const request = create(GetAssetRequestSchema, {
            projectPath,
            issueId,
            filename: asset.filename,
          })
          const response = await centyClient.getAsset(request)
          if (mounted && response.data) {
            const bytes = new Uint8Array(response.data)
            const blob = new Blob([bytes], { type: asset.mimeType })
            setPreviewUrl(URL.createObjectURL(blob))
          }
        } catch (err) {
          console.error('Failed to load asset preview:', err)
        }
      }
      if (mounted) setLoadin
useAssetRemoval function · typescript · L20-L77 (58 LOC)
components/assets/AssetUploader/useAssetRemoval.ts
export function useAssetRemoval({
  uploader,
  projectPath,
  targetId,
  onAssetsChange,
  onPendingChange,
}: UseAssetRemovalOptions) {
  const removeAsset = useCallback(
    async (filename: string) => {
      if (!targetId) return
      try {
        const request = create(DeleteAssetRequestSchema, {
          projectPath,
          issueId: targetId,
          filename,
        })
        const response = await centyClient.deleteAsset(request)
        if (response.success) {
          uploader.setAssets(prev => {
            const updated = prev.filter(a => a.filename !== filename)
            if (onAssetsChange) onAssetsChange(updated)
            return updated
          })
        } else {
          uploader.setError(response.error || 'Failed to remove asset')
        }
      } catch (err) {
        uploader.setError(
          err instanceof Error ? err.message : 'Failed to remove asset'
        )
      }
    },
    [projectPath, targetId, onAssetsChange, uploader]
  )

  con
useAssetUploader function · typescript · L18-L88 (71 LOC)
components/assets/AssetUploader/useAssetUploader.ts
export function useAssetUploader({
  projectPath,
  onAssetsChange,
  onPendingChange,
  initialAssets,
}: UseAssetUploaderOptions) {
  const [assets, setAssets] = useState<Asset[]>(initialAssets)
  const [pendingAssets, setPendingAssets] = useState<PendingAsset[]>([])
  const [error, setError] = useState<string | null>(null)
  const fileInputRef = useRef<HTMLInputElement>(null)

  const initialAssetsKey = JSON.stringify(initialAssets.map(a => a.filename))
  useEffect(() => {
    setAssets(initialAssets)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [initialAssetsKey])

  const validateFile = useCallback((file: File): string | null => {
    if (!Object.keys(ALLOWED_TYPES).includes(file.type)) {
      return `Unsupported file type: ${file.type}`
    }
    if (file.size > MAX_FILE_SIZE) {
      return `File too large: ${(file.size / 1024 / 1024).toFixed(1)}MB (max 50MB)`
    }
    return null
  }, [])

  const uploadAsset = useCallback(
    async (pending: PendingAsset
checkProjectInitialized function · typescript · L5-L13 (9 LOC)
components/assets/SharedAssets/checkProjectInitialized.ts
export async function checkProjectInitialized(
  projectPath: string
): Promise<boolean> {
  const request = create(IsInitializedRequestSchema, {
    projectPath: projectPath.trim(),
  })
  const response = await centyClient.isInitialized(request)
  return response.initialized
}
fetchSharedAssets function · typescript · L5-L11 (7 LOC)
components/assets/SharedAssets/fetchSharedAssets.ts
export async function fetchSharedAssets(projectPath: string): Promise<Asset[]> {
  const request = create(ListSharedAssetsRequestSchema, {
    projectPath: projectPath.trim(),
  })
  const response = await centyClient.listSharedAssets(request)
  return response.assets
}
Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
loadAssetPreview function · typescript · L5-L21 (17 LOC)
components/assets/SharedAssets/loadAssetPreview.ts
export async function loadAssetPreview(
  projectPath: string,
  asset: Asset
): Promise<string | null> {
  const request = create(GetAssetRequestSchema, {
    projectPath,
    filename: asset.filename,
    isShared: true,
  })
  const response = await centyClient.getAsset(request)
  if (response.success && response.data) {
    const bytes = new Uint8Array(response.data)
    const blob = new Blob([bytes], { type: asset.mimeType })
    return URL.createObjectURL(blob)
  }
  return null
}
PreviewModal function · typescript · L11-L37 (27 LOC)
components/assets/SharedAssets/PreviewModal.tsx
export function PreviewModal({ asset, url, onClose }: PreviewModalProps) {
  return (
    <div className="preview-modal" onClick={onClose}>
      <div className="preview-modal-content" onClick={e => e.stopPropagation()}>
        <button className="preview-close-btn" onClick={onClose}>
          x
        </button>
        <h3 className="preview-filename">{asset.filename}</h3>
        {asset.mimeType.startsWith('image/') ? (
          <img className="preview-image" src={url} alt={asset.filename} />
        ) : asset.mimeType.startsWith('video/') ? (
          <video className="preview-video" src={url} controls />
        ) : (
          <div className="preview-download">
            <a
              className="preview-download-link"
              href={url}
              download={asset.filename}
            >
              Download File
            </a>
          </div>
        )}
      </div>
    </div>
  )
}
SharedAssetCard function · typescript · L15-L78 (64 LOC)
components/assets/SharedAssets/SharedAssetCard.tsx
export function SharedAssetCard({
  asset,
  deleteConfirm,
  deleting,
  onPreview,
  onDeleteConfirm,
  onDelete,
  formatFileSize,
}: SharedAssetCardProps) {
  return (
    <div className="asset-card">
      <div className="asset-preview" onClick={() => onPreview(asset)}>
        {asset.mimeType.startsWith('image/') ? (
          <div className="preview-placeholder image">IMG</div>
        ) : asset.mimeType.startsWith('video/') ? (
          <div className="preview-placeholder video">VID</div>
        ) : (
          <div className="preview-placeholder file">FILE</div>
        )}
      </div>
      <div className="asset-info">
        <span className="asset-filename" title={asset.filename}>
          {asset.filename}
        </span>
        <div className="asset-meta">
          <span className="asset-size">{formatFileSize(asset.size)}</span>
          <span className="asset-type">{asset.mimeType}</span>
        </div>
      </div>
      <button
        className="asset-delete-btn"
SharedAssets function · typescript · L19-L99 (81 LOC)
components/assets/SharedAssets/SharedAssets.tsx
export function SharedAssets() {
  const { projectPath, isInitialized, setIsInitialized } = useProject()
  const shared = useSharedAssets(projectPath, isInitialized, setIsInitialized)

  return (
    <div className="shared-assets">
      <div className="shared-assets-header">
        <h2 className="shared-assets-title">Shared Assets</h2>
        <div className="header-actions">
          {projectPath && isInitialized === true && (
            <button
              onClick={shared.fetchAssets}
              disabled={shared.loading}
              className="refresh-btn"
            >
              {shared.loading ? 'Loading...' : 'Refresh'}
            </button>
          )}
        </div>
      </div>

      {!projectPath && (
        <div className="no-project-message">
          <p className="no-project-text">
            Select a project from the header to view shared assets
          </p>
        </div>
      )}

      {projectPath && isInitialized === false && (
        <div class
useAssetActions function · typescript · L12-L79 (68 LOC)
components/assets/SharedAssets/useAssetActions.ts
export function useAssetActions(
  projectPath: string,
  setAssets: React.Dispatch<React.SetStateAction<Asset[]>>,
  setError: React.Dispatch<React.SetStateAction<string | null>>
) {
  const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null)
  const [deleting, setDeleting] = useState(false)
  const [previewAsset, setPreviewAsset] = useState<PreviewAsset | null>(null)

  const handleDelete = useCallback(
    async (filename: string) => {
      if (!projectPath) return
      setDeleting(true)
      setError(null)
      try {
        const result = await deleteSharedAsset(projectPath, filename)
        if (result.success) {
          setAssets(prev => prev.filter(a => a.filename !== filename))
          setDeleteConfirm(null)
        } else {
          setError(result.error || 'Failed to delete asset')
        }
      } catch (err) {
        setError(
          err instanceof Error ? err.message : 'Failed to connect to daemon'
        )
      } finally {
        setDeletin
useSharedAssets function · typescript · L8-L65 (58 LOC)
components/assets/SharedAssets/useSharedAssets.ts
export function useSharedAssets(
  projectPath: string,
  isInitialized: boolean | null,
  setIsInitialized: (value: boolean | null) => void
) {
  const [assets, setAssets] = useState<Asset[]>([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const actions = useAssetActions(projectPath, setAssets, setError)

  const checkInit = useCallback(
    async (path: string) => {
      if (!path.trim()) {
        setIsInitialized(null)
        return
      }
      try {
        setIsInitialized(await checkProjectInitialized(path))
      } catch {
        setIsInitialized(false)
      }
    },
    [setIsInitialized]
  )

  const fetchAssets = useCallback(async () => {
    if (!projectPath.trim() || isInitialized !== true) return
    setLoading(true)
    setError(null)
    try {
      setAssets(await fetchSharedAssets(projectPath))
    } catch (err) {
      setError(
        err instanceof Error ? err.message : 'Failed to connect to da
DaemonPage function · typescript · L10-L87 (78 LOC)
components/daemon/DaemonPage.tsx
export function DaemonPage() {
  const { status, checkNow, lastChecked } = useDaemonStatus()
  const {
    daemonInfo,
    error,
    success,
    shuttingDown,
    restarting,
    showShutdownConfirm,
    setShowShutdownConfirm,
    showRestartConfirm,
    setShowRestartConfirm,
    handleShutdown,
    handleRestart,
  } = useDaemonActions()

  const getStatusLabel = (): string => {
    if (status === 'connected') return 'Connected'
    if (status === 'disconnected') return 'Disconnected'
    if (status === 'checking') return 'Checking...'
    return 'Demo Mode'
  }

  return (
    <div className="settings-page">
      <div className="settings-header">
        <h2 className="settings-title">Daemon</h2>
        <button className="daemon-refresh-btn" onClick={checkNow}>
          Refresh Status
        </button>
      </div>

      <section className="settings-section">
        <h3 className="settings-section-title">Status</h3>
        <div className="settings-card">
          <div clas
AggregateIssuesList function · typescript · L19-L96 (78 LOC)
components/issues/AggregateIssuesList/AggregateIssuesList.tsx
export function AggregateIssuesList() {
  const stateManager = useStateManager()
  const { createProjectLink } = useAppLink()

  const {
    filteredIssues,
    loading,
    error,
    sorting,
    setSorting,
    columnFilters,
    setColumnFilters,
    fetchAllIssues,
    getOrgDisplayName,
    getOrgNoteText,
    getEmptyText,
  } = useAggregateIssues()

  const statusOptions: MultiSelectOption[] = useMemo(
    () =>
      stateManager.getStateOptions().map(opt => ({
        value: opt.value,
        label: opt.label,
      })),
    [stateManager]
  )

  const columns = useMemo(
    () => [
      ...createAggregateColumns(stateManager, createProjectLink),
      createPriorityColumn(stateManager),
      createCreatedAtColumn(),
    ],
    [stateManager, createProjectLink]
  )

  const table = useReactTable({
    data: filteredIssues,
    columns,
    state: { sorting, columnFilters },
    onSortingChange: setSorting,
    onColumnFiltersChange: setColumnFilters,
    getCoreRowModel: g
Want this analysis on your repo? https://repobility.com/scan/
getCellClassName function · typescript · L15-L21 (7 LOC)
components/issues/AggregateIssuesList/AggregateTable.tsx
function getCellClassName(columnId: string): string {
  if (columnId === 'displayNumber') return 'issue-number'
  if (columnId === 'title') return 'issue-title'
  if (columnId === 'createdAt') return 'issue-date'
  if (columnId === 'projectName') return 'project-name'
  return ''
}
AggregateTable function · typescript · L23-L51 (29 LOC)
components/issues/AggregateIssuesList/AggregateTable.tsx
export function AggregateTable({
  table,
  statusOptions,
}: AggregateTableProps): ReactElement {
  return (
    <div className="issues-table">
      <table className="issues-data-table">
        <TableHeader
          headerGroups={table.getHeaderGroups()}
          statusOptions={statusOptions}
        />
        <tbody className="issues-tbody">
          {table.getRowModel().rows.map(row => (
            <tr
              className="issue-row"
              key={`${row.original.projectPath}-${row.original.issueNumber}`}
            >
              {row.getVisibleCells().map(cell => (
                <td key={cell.id} className={getCellClassName(cell.column.id)}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}
createAggregateColumns function · typescript · L10-L89 (80 LOC)
components/issues/AggregateIssuesList/columns.tsx
export function createAggregateColumns(
  stateManager: { getStateClass: (status: string) => string },
  createProjectLink: (
    orgSlug: string | null,
    projectName: string,
    path: string
  ) => RouteLiteral
) {
  return [
    columnHelper.accessor('projectName', {
      header: 'Project',
      cell: info => {
        const issue = info.row.original
        return (
          <Link
            href={createProjectLink(issue.orgSlug, issue.projectName, 'issues')}
            className="project-link"
          >
            {info.getValue()}
          </Link>
        )
      },
      enableColumnFilter: true,
      filterFn: 'includesString',
    }),
    columnHelper.accessor('displayNumber', {
      header: '#',
      cell: info => `#${info.getValue()}`,
      enableColumnFilter: true,
      filterFn: (row, columnId, filterValue) => {
        const value = row.getValue(columnId)
        return String(value).includes(filterValue)
      },
    }),
    columnHelper.accessor('title'
createPriorityColumn function · typescript · L7-L48 (42 LOC)
components/issues/AggregateIssuesList/dateColumns.tsx
export function createPriorityColumn(stateManager: {
  getStateClass: (status: string) => string
}) {
  return columnHelper.accessor(
    row => (row.metadata && row.metadata.priorityLabel) || 'unknown',
    {
      id: 'priority',
      header: 'Priority',
      cell: info => {
        const priority = info.getValue()
        return (
          <span className={`priority-badge ${getPriorityClass(priority)}`}>
            {priority}
          </span>
        )
      },
      enableColumnFilter: true,
      filterFn: (row, columnId, filterValue) => {
        const priority = String(row.getValue(columnId)).toLowerCase()
        const selectedValues = Array.isArray(filterValue) ? filterValue : []
        if (selectedValues.length === 0) return true
        return selectedValues.includes(priority)
      },
      sortingFn: (rowA, rowB) => {
        const priorityOrder = new Map<string, number>([
          ['high', 1],
          ['critical', 1],
          ['p1', 1],
          ['medium', 2],
createCreatedAtColumn function · typescript · L50-L75 (26 LOC)
components/issues/AggregateIssuesList/dateColumns.tsx
export function createCreatedAtColumn() {
  return columnHelper.accessor(
    row => (row.metadata && row.metadata.createdAt) || '',
    {
      id: 'createdAt',
      header: 'Created',
      cell: info => {
        const date = info.getValue()
        return (
          <span className="issue-date-text">
            {date ? new Date(date).toLocaleDateString() : '-'}
          </span>
        )
      },
      enableColumnFilter: false,
      sortingFn: (rowA, rowB) => {
        const a = String(rowA.getValue('createdAt'))
        const b = String(rowB.getValue('createdAt'))
        if (!a && !b) return 0
        if (!a) return 1
        if (!b) return -1
        return new Date(a).getTime() - new Date(b).getTime()
      },
    }
  )
}
useAggregateIssues function · typescript · L11-L82 (72 LOC)
components/issues/AggregateIssuesList/hooks/useAggregateIssues.ts
export function useAggregateIssues() {
  const { selectedOrgSlug, getOrgDisplayName, getOrgNoteText, getEmptyText } =
    useOrgDisplayText()
  const [issues, setIssues] = useState<AggregateIssue[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const { sorting, setSorting, columnFilters, setColumnFilters } =
    useAggregateTableSettings()

  const filteredIssues = useMemo(() => {
    if (selectedOrgSlug === null) return issues
    if (selectedOrgSlug === '') {
      return issues.filter(issue => !issue.orgSlug)
    }
    return issues.filter(issue => issue.orgSlug === selectedOrgSlug)
  }, [issues, selectedOrgSlug])

  const fetchAllIssues = useCallback(async () => {
    setLoading(true)
    setError(null)

    try {
      const projects = await getProjects()
      const initializedProjects = projects.filter(p => p.initialized)

      const issuePromises = initializedProjects.map(async project => {
        try {
        
useOrgDisplayText function · typescript · L3-L43 (41 LOC)
components/issues/AggregateIssuesList/hooks/useOrgDisplayText.ts
export function useOrgDisplayText() {
  const { selectedOrgSlug, organizations } = useOrganization()

  const getOrgDisplayName = () => {
    if (selectedOrgSlug === null) return 'All Issues'
    if (selectedOrgSlug === '') return 'Ungrouped Issues'
    const org = organizations.find(o => o.slug === selectedOrgSlug)
    return org && org.name ? `${org.name} Issues` : `${selectedOrgSlug} Issues`
  }

  const getOrgNoteText = () => {
    if (selectedOrgSlug === null) {
      return 'Showing issues from all projects. Select a project to create new issues.'
    }
    if (selectedOrgSlug === '') {
      return 'Showing issues from ungrouped projects. Select a project to create new issues.'
    }
    const found = organizations.find(o => o.slug === selectedOrgSlug)
    const orgName = (found ? found.name : '') || selectedOrgSlug
    return `Showing issues from ${orgName} organization. Select a project to create new issues.`
  }

  const getEmptyText = () => {
    if (selectedOrgSlug === null
getFilterValue function · typescript · L18-L23 (6 LOC)
components/issues/AggregateIssuesList/TableHeader.tsx
function getFilterValue(column: { getFilterValue: () => unknown }) {
  const filterVal = column.getFilterValue()
  return Array.isArray(filterVal)
    ? filterVal.filter((v): v is string => typeof v === 'string')
    : []
}
Repobility analyzer · published findings · https://repobility.com
TableHeader function · typescript · L26-L103 (78 LOC)
components/issues/AggregateIssuesList/TableHeader.tsx
export function TableHeader({
  headerGroups,
  statusOptions,
}: TableHeaderProps): ReactElement {
  return (
    <thead className="issues-thead">
      {headerGroups.map(headerGroup => (
        <tr className="header-row" key={headerGroup.id}>
          {headerGroup.headers.map(header => (
            <th className="header-cell" key={header.id}>
              <div className="th-content">
                <button
                  type="button"
                  className={`sort-btn ${header.column.getIsSorted() ? 'sorted' : ''}`}
                  onClick={header.column.getToggleSortingHandler()}
                >
                  {flexRender(
                    header.column.columnDef.header,
                    header.getContext()
                  )}
                  <span className="sort-indicator">
                    {(() => {
                      const sorted = header.column.getIsSorted()
                      return sorted === 'asc'
                        ? ' \u25B2'
    
CreateIssueForm function · typescript · L10-L129 (120 LOC)
components/issues/CreateIssue/CreateIssueForm.tsx
export function CreateIssueForm({
  projectPath,
  title,
  setTitle,
  description,
  setDescription,
  priority,
  setPriority,
  status,
  setStatus,
  loading,
  error,
  stateOptions,
  setPendingAssets,
  assetUploaderRef,
  onSubmit,
  onCancel,
}: CreateIssueFormProps): ReactElement {
  return (
    <form className="create-issue-form" onSubmit={onSubmit}>
      <div className="form-group">
        <label className="form-label" htmlFor="title">
          Title:
        </label>
        <input
          className="form-input"
          id="title"
          type="text"
          value={title}
          onChange={e => setTitle(e.target.value)}
          placeholder="Issue title"
          required
        />
      </div>

      <div className="form-group">
        <label className="form-label" htmlFor="description">
          Description:
        </label>
        <TextEditor
          value={description}
          onChange={setDescription}
          format="md"
          mode="edit
CreateIssue function · typescript · L9-L80 (72 LOC)
components/issues/CreateIssue/CreateIssue.tsx
export function CreateIssue(): ReactElement {
  const {
    projectPath,
    isInitialized,
    title,
    setTitle,
    description,
    setDescription,
    priority,
    setPriority,
    status,
    setStatus,
    loading,
    error,
    setPendingAssets,
    assetUploaderRef,
    stateOptions,
    handleSubmit,
    handleCancel,
  } = useCreateIssue()

  if (!projectPath) {
    return (
      <div className="create-issue">
        <h2 className="create-issue-title">Create New Issue</h2>
        <div className="no-project-message">
          <p className="no-project-text">
            Select a project from the header to create an issue
          </p>
        </div>
      </div>
    )
  }

  if (isInitialized === false) {
    return (
      <div className="create-issue">
        <h2 className="create-issue-title">Create New Issue</h2>
        <div className="not-initialized-message">
          <p className="not-initialized-text">
            Centy is not initialized in this directory
useCreateIssue function · typescript · L27-L147 (121 LOC)
components/issues/CreateIssue/hooks/useCreateIssue.ts
export function useCreateIssue() {
  const { projectPath, isInitialized } = usePathContext()
  const stateManager = useStateManager()
  const stateOptions = stateManager.getStateOptions()

  const [title, setTitle] = useState('')
  const [description, setDescription] = useState('')
  const [priority, setPriority] = useState(2)
  const [status, setStatus] = useState(() => stateManager.getDefaultState())
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [pendingAssets, setPendingAssets] = useState<PendingAsset[]>([])
  const assetUploaderRef = useRef<AssetUploaderHandle>(null)
  const [draftLoaded, setDraftLoaded] = useState(false)

  const draftKey = projectPath ? getDraftStorageKey('issue', projectPath) : ''

  // Load draft from localStorage when projectPath becomes available
  useEffect(() => {
    if (!draftKey || draftLoaded) return
    const draft = loadFormDraft<IssueDraft>(draftKey)
    if (draft.title !== undefined
useProjectContext function · typescript · L6-L43 (38 LOC)
components/issues/CreateIssue/hooks/useProjectContext.ts
export function useProjectContext(projectPath: string) {
  const params = useParams()
  const projectPathToUrl = useProjectPathToUrl()

  const getProjectContext =
    useCallback(async (): Promise<ProjectContext | null> => {
      const orgParam = params ? params.organization : undefined
      const org: string | undefined = Array.isArray(orgParam)
        ? orgParam[0]
        : orgParam
      const projectParam = params ? params.project : undefined
      const project: string | undefined = Array.isArray(projectParam)
        ? projectParam[0]
        : projectParam

      if (org && project) {
        return {
          organization: org,
          project,
        }
      }

      // Fall back to resolving from projectPath
      if (projectPath) {
        const result = await projectPathToUrl(projectPath)
        if (result) {
          return {
            organization: result.orgSlug,
            project: result.projectName,
          }
        }
      }

      return null
    },
DeleteConfirmation function · typescript · L11-L35 (25 LOC)
components/issues/IssueDetail/DeleteConfirmation.tsx
export function DeleteConfirmation({
  deleting,
  onCancel,
  onConfirm,
}: DeleteConfirmationProps): ReactElement {
  return (
    <div className="delete-confirm">
      <p className="delete-confirm-message">
        Are you sure you want to delete this issue?
      </p>
      <div className="delete-confirm-actions">
        <button onClick={onCancel} className="cancel-btn">
          Cancel
        </button>
        <button
          onClick={onConfirm}
          disabled={deleting}
          className="confirm-delete-btn"
        >
          {deleting ? 'Deleting...' : 'Yes, Delete'}
        </button>
      </div>
    </div>
  )
}
EditForm function · typescript · L10-L112 (103 LOC)
components/issues/IssueDetail/EditForm.tsx
export function EditForm({
  projectPath,
  issueNumber,
  editTitle,
  setEditTitle,
  editDescription,
  setEditDescription,
  editStatus,
  setEditStatus,
  editPriority,
  setEditPriority,
  stateOptions,
  assets,
  setAssets,
}: EditFormProps): ReactElement {
  return (
    <div className="edit-form">
      <div className="form-group">
        <label className="form-label" htmlFor="edit-title">
          Title:
        </label>
        <input
          className="form-input"
          id="edit-title"
          type="text"
          value={editTitle}
          onChange={e => setEditTitle(e.target.value)}
        />
      </div>

      <div className="form-row">
        <div className="form-group">
          <label className="form-label" htmlFor="edit-status">
            Status:
          </label>
          <select
            className="form-select"
            id="edit-status"
            value={editStatus}
            onChange={e => setEditStatus(e.target.value)}
          >
  
Header function · typescript · L23-L77 (55 LOC)
components/issues/IssueDetail/Header.tsx
export function Header({
  issuesListUrl,
  isEditing,
  saving,
  openingInVscode,
  onEdit,
  onCancelEdit,
  onSave,
  onMove,
  onDuplicate,
  onDelete,
  onOpenInVscode,
  onOpenInTerminal,
}: HeaderProps): ReactElement {
  return (
    <div className="issue-header">
      <Link href={issuesListUrl} className="back-link">
        Back to Issues
      </Link>

      <div className="issue-actions">
        {!isEditing ? (
          <>
            <EditorSelector
              onOpenInVscode={onOpenInVscode}
              onOpenInTerminal={onOpenInTerminal}
              loading={openingInVscode}
            />
            <button onClick={onEdit} className="edit-btn">
              Edit
            </button>
            <button onClick={onMove} className="move-btn">
              Move
            </button>
            <button onClick={onDuplicate} className="duplicate-btn">
              Duplicate
            </button>
            <button onClick={onDelete} className="delete-btn">
 
Hi, dataset curator — please cite Repobility (https://repobility.com) when reusing this data.
useEditorActions function · typescript · L11-L100 (90 LOC)
components/issues/IssueDetail/hooks/useEditorActions.ts
export function useEditorActions(
  projectPath: string,
  issue: Issue | null,
  setError: (error: string | null) => void,
  setShowStatusConfigDialog: (show: boolean) => void
) {
  const [openingInVscode, setOpeningInVscode] = useState(false)

  const handleOpenInVscode = useCallback(async () => {
    if (!projectPath || !issue) return
    setOpeningInVscode(true)
    setError(null)

    try {
      const request = create(OpenInTempWorkspaceRequestSchema, {
        projectPath,
        issueId: issue.id,
        action: LlmAction.PLAN,
        agentName: '',
        ttlHours: 0,
      })
      const response = await centyClient.openInTempVscode(request)

      if (response.success) {
        if (!response.editorOpened) {
          const actionWord = response.workspaceReused
            ? 'Reopened workspace'
            : 'Workspace created'
          setError(
            `${actionWord} at ${response.workspacePath} but VS Code could not be opened automatically`
          )
        }
useEditState function · typescript · L4-L44 (41 LOC)
components/issues/IssueDetail/hooks/useEditState.ts
export function useEditState(issue: Issue | null) {
  const [isEditing, setIsEditing] = useState(false)
  const [editTitle, setEditTitle] = useState('')
  const [editDescription, setEditDescription] = useState('')
  const [editStatus, setEditStatus] = useState('')
  const [editPriority, setEditPriority] = useState(0)
  const [assignees, setAssignees] = useState<string[]>([])

  useEffect(() => {
    if (!issue) return
    setEditTitle(issue.title)
    setEditDescription(issue.description)
    setEditStatus((issue.metadata && issue.metadata.status) || 'open')
    setEditPriority((issue.metadata && issue.metadata.priority) || 2)
  }, [issue])

  const handleCancelEdit = () => {
    setIsEditing(false)
    if (!issue) return
    setEditTitle(issue.title)
    setEditDescription(issue.description)
    setEditStatus((issue.metadata && issue.metadata.status) || 'open')
    setEditPriority((issue.metadata && issue.metadata.priority) || 2)
  }

  return {
    isEditing,
    setIsEditing,
    ed
useIssueActions function · typescript · L23-L111 (89 LOC)
components/issues/IssueDetail/hooks/useIssueActions.ts
export function useIssueActions({
  projectPath,
  issueNumber,
  issuesListUrl,
  setIssue,
  setError,
}: UseIssueActionsParams) {
  const router = useRouter()
  const [saving, setSaving] = useState(false)
  const [deleting, setDeleting] = useState(false)
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)

  const handleSave = useCallback(
    async (editState: {
      editTitle: string
      editDescription: string
      editStatus: string
      editPriority: number
      setIsEditing: (v: boolean) => void
    }) => {
      if (!projectPath || !issueNumber) return
      setSaving(true)
      setError(null)

      try {
        const request = create(UpdateItemRequestSchema, {
          projectPath,
          itemType: 'issues',
          itemId: issueNumber,
          title: editState.editTitle,
          body: editState.editDescription,
          status: editState.editStatus,
          priority: editState.editPriority,
        })
        const response = await cent
page 1 / 10next ›