Function bodies 471 total
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' && (
<divuploadAssetToServer 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) setLoadinuseAssetRemoval 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]
)
conuseAssetUploader 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: PendingAssetcheckProjectInitialized 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 classuseAssetActions 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 {
setDeletinuseSharedAssets 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 daDaemonPage 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 clasAggregateIssuesList 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: gWant 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 === nullgetFilterValue 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="editCreateIssue 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 !== undefineduseProjectContext 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,
eduseIssueActions 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 centpage 1 / 10next ›