Function bodies 234 total
handleCreate function · typescript · L34-L51 (18 LOC)src/components/create-rock-dialog.tsx
async function handleCreate() {
if (!title.trim() || !quarterId || !user) return
await supabase.from('rocks').insert({
title: title.trim(),
owner_id: user.id,
group_id: groupId,
quarter_id: quarterId,
target_completion_date: targetDate || null,
notes: notes || null,
})
setTitle('')
setTargetDate('')
setNotes('')
setOpen(false)
mutate(`rocks-${groupId}-${quarterId}`)
}CreateRockIdeaDialog function · typescript · L17-L84 (68 LOC)src/components/create-rock-idea-dialog.tsx
export function CreateRockIdeaDialog({ groupId }: CreateRockIdeaDialogProps) {
const [open, setOpen] = useState(false)
const [description, setDescription] = useState('')
const [suggestedOwner, setSuggestedOwner] = useState<string | null>(null)
const [priorityColor, setPriorityColor] = useState<string>('green')
const [comments, setComments] = useState('')
const supabase = createClient()
async function handleCreate() {
if (!description.trim()) return
await supabase.from('rock_ideas').insert({
group_id: groupId,
description: description.trim(),
suggested_owner_id: suggestedOwner,
priority_color: priorityColor,
comments: comments || null,
})
setDescription('')
setSuggestedOwner(null)
setPriorityColor('green')
setComments('')
setOpen(false)
mutate('rock-ideas')
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>New Rock Idea</Button>
</DialogTrihandleCreate function · typescript · L25-L42 (18 LOC)src/components/create-rock-idea-dialog.tsx
async function handleCreate() {
if (!description.trim()) return
await supabase.from('rock_ideas').insert({
group_id: groupId,
description: description.trim(),
suggested_owner_id: suggestedOwner,
priority_color: priorityColor,
comments: comments || null,
})
setDescription('')
setSuggestedOwner(null)
setPriorityColor('green')
setComments('')
setOpen(false)
mutate('rock-ideas')
}CreateTodoDialog function · typescript · L17-L72 (56 LOC)src/components/create-todo-dialog.tsx
export function CreateTodoDialog({ groupId, sourceIssueId }: CreateTodoDialogProps) {
const [open, setOpen] = useState(false)
const [description, setDescription] = useState('')
const [assignedTo, setAssignedTo] = useState<string | null>(null)
const [dueDate, setDueDate] = useState(() => {
const d = new Date()
d.setDate(d.getDate() + 7)
return d.toISOString().split('T')[0]
})
const supabase = createClient()
async function handleCreate() {
if (!description.trim() || !assignedTo || !dueDate) return
await supabase.from('todos').insert({
group_id: groupId,
description: description.trim(),
assigned_to_id: assignedTo,
due_date: dueDate,
source_issue_id: sourceIssueId || null,
})
setDescription('')
setAssignedTo(null)
setOpen(false)
mutate(`todos-${groupId}`)
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline" size="sm">New To-Do</BuhandleCreate function · typescript · L28-L43 (16 LOC)src/components/create-todo-dialog.tsx
async function handleCreate() {
if (!description.trim() || !assignedTo || !dueDate) return
await supabase.from('todos').insert({
group_id: groupId,
description: description.trim(),
assigned_to_id: assignedTo,
due_date: dueDate,
source_issue_id: sourceIssueId || null,
})
setDescription('')
setAssignedTo(null)
setOpen(false)
mutate(`todos-${groupId}`)
}DashboardCard function · typescript · L36-L76 (41 LOC)src/components/dashboard-grid.tsx
export function DashboardCard({ title, value, subtitle, icon, accent, progress, href, onClick }: DashboardCardProps) {
const borderClass = accent ? `border-l-4 ${accentBorderMap[accent]}` : ''
const progressBarColor = accent ? accentProgressMap[accent] : 'bg-primary'
const isClickable = !!(href || onClick)
const cardContent = (
<Card className={`card-hover animate-fade-in h-full ${borderClass} ${isClickable ? 'cursor-pointer group' : ''}`}>
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle className="text-sm font-medium text-muted-foreground">{title}</CardTitle>
<div className="flex items-center gap-1">
{icon && <span className="text-muted-foreground">{icon}</span>}
{href && <ArrowUpRight className="h-4 w-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" />}
</div>
</div>
</CardHeader>
<CardContent>
<p classNRocksByGroupTable function · typescript · L87-L142 (56 LOC)src/components/dashboard-grid.tsx
export function RocksByGroupTable({ data }: { data: RocksByGroupRow[] }) {
return (
<Card className="card-hover animate-fade-in">
<CardHeader>
<CardTitle className="text-base">Rocks by Group</CardTitle>
</CardHeader>
<CardContent>
<div className="table-striped overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b">
<th className="text-left py-2 font-medium">Group</th>
<th className="text-right py-2 font-medium">Total</th>
<th className="text-right py-2 font-medium">On Track</th>
<th className="text-right py-2 font-medium">Off Track</th>
<th className="text-right py-2 font-medium">% On Track</th>
</tr>
</thead>
<tbody>
{data.map((row) => (
<tr key={row.groupName} className="border-b hover:bg-muted/50">
<td className="pGenerated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
RocksByPersonTable function · typescript · L153-L198 (46 LOC)src/components/dashboard-grid.tsx
export function RocksByPersonTable({ data }: { data: RocksByPersonRow[] }) {
return (
<Card className="card-hover animate-fade-in">
<CardHeader>
<CardTitle className="text-base">Rocks by Person</CardTitle>
</CardHeader>
<CardContent>
<div className="table-striped overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b">
<th className="text-left py-2 font-medium">Person</th>
<th className="text-right py-2 font-medium">Total</th>
<th className="text-right py-2 font-medium">On Track</th>
<th className="text-right py-2 font-medium">Off Track</th>
</tr>
</thead>
<tbody>
{data.map((row) => (
<tr key={row.personName} className="border-b hover:bg-muted/50">
<td className="py-2">
{row.groupId && row.ownerId ? (
EmptyState function · typescript · L14-L27 (14 LOC)src/components/empty-state.tsx
export function EmptyState({ icon, title, description, action }: EmptyStateProps) {
return (
<div className="flex flex-col items-center justify-center py-16 px-4 animate-fade-in">
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-primary/10 text-primary mb-4">
{icon}
</div>
<h3 className="text-lg font-semibold mb-1">{title}</h3>
<p className="text-sm text-muted-foreground text-center max-w-sm mb-4">{description}</p>
{action && (
<Button onClick={action.onClick}>{action.label}</Button>
)}
</div>
)
}FilterChip function · typescript · L11-L25 (15 LOC)src/components/filter-chip.tsx
export function FilterChip({ label, value, onClear }: FilterChipProps) {
return (
<span className="inline-flex items-center gap-1 rounded-full bg-primary/10 text-primary px-3 py-1 text-sm animate-fade-in">
<span className="text-muted-foreground text-xs">{label}:</span>
<span className="font-medium">{value}</span>
<button
onClick={onClear}
className="ml-1 rounded-full hover:bg-primary/20 p-0.5 transition-colors"
aria-label={`Clear ${label} filter`}
>
<X className="h-3 w-3" />
</button>
</span>
)
}FocusTable function · typescript · L30-L152 (123 LOC)src/components/focus-table.tsx
export function FocusTable({ snapshot, readOnly = false, showOwner = false, ownerName }: FocusTableProps) {
const supabase = createClient()
const [newSubject, setNewSubject] = useState('')
const items = snapshot?.focus_items?.sort((a: any, b: any) => (a.sort_order || 0) - (b.sort_order || 0)) || []
async function updateItem(itemId: string, field: string, value: any) {
const updateValue = field === 'prospect_value' ? (value ? Number(value) : null) : (value || null)
await supabase.from('focus_items').update({ [field]: updateValue }).eq('id', itemId)
mutate(`focus-${snapshot.user_id}-${snapshot.group_id}-${snapshot.week_date}`)
mutate(`group-focus-${snapshot.group_id}-${snapshot.week_date}`)
}
async function addItem() {
if (!newSubject.trim() || !snapshot) return
const maxOrder = items.reduce((max: number, i: any) => Math.max(max, i.sort_order || 0), -1)
await supabase.from('focus_items').insert({
snapshot_id: snapshot.id,
company_subjupdateItem function · typescript · L35-L40 (6 LOC)src/components/focus-table.tsx
async function updateItem(itemId: string, field: string, value: any) {
const updateValue = field === 'prospect_value' ? (value ? Number(value) : null) : (value || null)
await supabase.from('focus_items').update({ [field]: updateValue }).eq('id', itemId)
mutate(`focus-${snapshot.user_id}-${snapshot.group_id}-${snapshot.week_date}`)
mutate(`group-focus-${snapshot.group_id}-${snapshot.week_date}`)
}addItem function · typescript · L42-L52 (11 LOC)src/components/focus-table.tsx
async function addItem() {
if (!newSubject.trim() || !snapshot) return
const maxOrder = items.reduce((max: number, i: any) => Math.max(max, i.sort_order || 0), -1)
await supabase.from('focus_items').insert({
snapshot_id: snapshot.id,
company_subject: newSubject.trim(),
sort_order: maxOrder + 1,
})
setNewSubject('')
mutate(`focus-${snapshot.user_id}-${snapshot.group_id}-${snapshot.week_date}`)
}deleteItem function · typescript · L54-L57 (4 LOC)src/components/focus-table.tsx
async function deleteItem(itemId: string) {
await supabase.from('focus_items').delete().eq('id', itemId)
mutate(`focus-${snapshot.user_id}-${snapshot.group_id}-${snapshot.week_date}`)
}IssueBoard function · typescript · L16-L129 (114 LOC)src/components/issue-board.tsx
export function IssueBoard({ groupId, showClosed = true }: IssueBoardProps) {
const supabase = createClient()
const [expandedId, setExpandedId] = useState<string | null>(null)
const { data: issues, isLoading } = useSWR(`issues-${groupId}`, async () => {
const { data, error } = await supabase
.from('issues')
.select('*, raised_by_user:profiles!raised_by(full_name), assigned_to:profiles!assigned_to_id(full_name)')
.eq('group_id', groupId)
.eq('is_archived', false)
.order('priority', { ascending: true, nullsFirst: false })
.order('created_at', { ascending: false })
if (error) throw error
return data
}, { refreshInterval: 30000 })
async function updateStatus(issueId: string, status: string) {
const updates: any = { status }
if (status === 'closed') updates.closed_at = new Date().toISOString()
await supabase.from('issues').update(updates).eq('id', issueId)
mutate(`issues-${groupId}`)
}
async function updateResoRepobility — the code-quality scanner for AI-generated software · https://repobility.com
updateStatus function · typescript · L32-L37 (6 LOC)src/components/issue-board.tsx
async function updateStatus(issueId: string, status: string) {
const updates: any = { status }
if (status === 'closed') updates.closed_at = new Date().toISOString()
await supabase.from('issues').update(updates).eq('id', issueId)
mutate(`issues-${groupId}`)
}updateResolution function · typescript · L39-L42 (4 LOC)src/components/issue-board.tsx
async function updateResolution(issueId: string, notes: string) {
await supabase.from('issues').update({ resolution_notes: notes }).eq('id', issueId)
mutate(`issues-${groupId}`)
}IssueDetailDialog function · typescript · L19-L127 (109 LOC)src/components/issue-detail-dialog.tsx
export function IssueDetailDialog({ issue, groupId, open, onOpenChange }: IssueDetailDialogProps) {
const supabase = createClient()
const [status, setStatus] = useState(issue?.status || 'open')
const [resolution, setResolution] = useState(issue?.resolution_notes || '')
// Reset state when issue changes
if (issue && status !== issue.status && !open) {
setStatus(issue.status)
setResolution(issue.resolution_notes || '')
}
async function handleStatusChange(newStatus: string) {
setStatus(newStatus)
const updates: any = { status: newStatus }
if (newStatus === 'closed') updates.closed_at = new Date().toISOString()
await supabase.from('issues').update(updates).eq('id', issue.id)
mutate(`issues-${groupId}`)
}
async function handleSaveResolution() {
await supabase.from('issues').update({ resolution_notes: resolution }).eq('id', issue.id)
mutate(`issues-${groupId}`)
}
if (!issue) return null
const statusColors: Record<string, stringhandleStatusChange function · typescript · L30-L36 (7 LOC)src/components/issue-detail-dialog.tsx
async function handleStatusChange(newStatus: string) {
setStatus(newStatus)
const updates: any = { status: newStatus }
if (newStatus === 'closed') updates.closed_at = new Date().toISOString()
await supabase.from('issues').update(updates).eq('id', issue.id)
mutate(`issues-${groupId}`)
}handleSaveResolution function · typescript · L38-L41 (4 LOC)src/components/issue-detail-dialog.tsx
async function handleSaveResolution() {
await supabase.from('issues').update({ resolution_notes: resolution }).eq('id', issue.id)
mutate(`issues-${groupId}`)
}MeetingAgenda function · typescript · L20-L69 (50 LOC)src/components/meeting-agenda.tsx
export function MeetingAgenda({ activeStep, onStepChange, completedSteps = [] }: MeetingAgendaProps) {
return (
<nav className="space-y-0">
{AGENDA_STEPS.map((step, index) => {
const isCompleted = completedSteps.includes(step.id)
const isActive = activeStep === step.id
return (
<div key={step.id}>
{index > 0 && (
<div className="flex justify-start pl-[15px]">
<div className={cn(
'w-0.5 h-3',
completedSteps.includes(AGENDA_STEPS[index - 1].id) ? 'bg-green-500' : 'bg-border'
)} />
</div>
)}
<button
onClick={() => onStepChange(step.id)}
className={cn(
'w-full text-left rounded-md px-3 py-2 text-sm transition-all duration-200',
isActive ? 'bg-primary/10' : 'hover:bg-accent'
)}
>
<div className="flex items-center gapMilestoneList function · typescript · L16-L231 (216 LOC)src/components/milestone-list.tsx
export function MilestoneList({ rockId, readOnly = false }: MilestoneListProps) {
const supabase = createClient()
const [newTitle, setNewTitle] = useState('')
const { data: milestones, isLoading } = useSWR(`milestones-${rockId}`, async () => {
const { data, error } = await supabase
.from('milestones')
.select('*, collaborators:milestone_collaborators(user_id, profiles(full_name))')
.eq('rock_id', rockId)
.order('sort_order', { ascending: true })
if (error) throw error
return data
})
async function addMilestone() {
if (!newTitle.trim()) return
const maxOrder = milestones?.reduce((max: number, m: any) => Math.max(max, m.sort_order), -1) ?? -1
await supabase.from('milestones').insert({
rock_id: rockId,
title: newTitle.trim(),
sort_order: maxOrder + 1,
})
setNewTitle('')
mutate(`milestones-${rockId}`)
}
async function updateMilestone(id: string, field: string, value: any) {
await supabase.froaddMilestone function · typescript · L30-L40 (11 LOC)src/components/milestone-list.tsx
async function addMilestone() {
if (!newTitle.trim()) return
const maxOrder = milestones?.reduce((max: number, m: any) => Math.max(max, m.sort_order), -1) ?? -1
await supabase.from('milestones').insert({
rock_id: rockId,
title: newTitle.trim(),
sort_order: maxOrder + 1,
})
setNewTitle('')
mutate(`milestones-${rockId}`)
}About: code-quality intelligence by Repobility · https://repobility.com
updateMilestone function · typescript · L42-L45 (4 LOC)src/components/milestone-list.tsx
async function updateMilestone(id: string, field: string, value: any) {
await supabase.from('milestones').update({ [field]: value }).eq('id', id)
mutate(`milestones-${rockId}`)
}deleteMilestone function · typescript · L47-L50 (4 LOC)src/components/milestone-list.tsx
async function deleteMilestone(id: string) {
await supabase.from('milestones').delete().eq('id', id)
mutate(`milestones-${rockId}`)
}moveMilestone function · typescript · L52-L72 (21 LOC)src/components/milestone-list.tsx
async function moveMilestone(index: number, direction: 'up' | 'down') {
if (!milestones) return
const newIndex = direction === 'up' ? index - 1 : index + 1
if (newIndex < 0 || newIndex >= milestones.length) return
const updates = milestones.map((m: any, i: number) => {
let newOrder = m.sort_order
if (i === index) newOrder = milestones[newIndex].sort_order
if (i === newIndex) newOrder = milestones[index].sort_order
return { id: m.id, sort_order: newOrder }
})
await Promise.all(
updates
.filter((u: any, i: number) => u.sort_order !== milestones[i].sort_order)
.map((u: any) =>
supabase.from('milestones').update({ sort_order: u.sort_order }).eq('id', u.id)
)
)
mutate(`milestones-${rockId}`)
}CardSkeleton function · typescript · L3-L11 (9 LOC)src/components/page-skeleton.tsx
export function CardSkeleton() {
return (
<div className="rounded-lg border bg-card p-4 space-y-3">
<Skeleton className="h-4 w-1/3" />
<Skeleton className="h-8 w-1/2" />
<Skeleton className="h-3 w-1/4" />
</div>
)
}TableSkeleton function · typescript · L13-L29 (17 LOC)src/components/page-skeleton.tsx
export function TableSkeleton({ rows = 5 }: { rows?: number }) {
return (
<div className="rounded-lg border bg-card p-4 space-y-3">
<Skeleton className="h-5 w-1/4 mb-4" />
<div className="space-y-2">
{Array.from({ length: rows }).map((_, i) => (
<div key={i} className="flex gap-4">
<Skeleton className="h-4 w-1/4" />
<Skeleton className="h-4 w-1/6" />
<Skeleton className="h-4 w-1/6" />
<Skeleton className="h-4 w-1/6" />
</div>
))}
</div>
</div>
)
}KpiSkeleton function · typescript · L31-L38 (8 LOC)src/components/page-skeleton.tsx
export function KpiSkeleton() {
return (
<div className="rounded-lg border bg-card p-4 space-y-2 border-l-4 border-l-muted">
<Skeleton className="h-3 w-1/2" />
<Skeleton className="h-7 w-1/3" />
</div>
)
}Providers function · typescript · L5-L11 (7 LOC)src/components/providers.tsx
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider attribute="class" defaultTheme="light" enableSystem={false}>
{children}
</ThemeProvider>
)
}QuarterSelector function · typescript · L11-L30 (20 LOC)src/components/quarter-selector.tsx
export function QuarterSelector({ value, onChange }: QuarterSelectorProps) {
const { data: quarters, isLoading } = useQuarters()
if (isLoading || !quarters) return null
return (
<Select value={value || undefined} onValueChange={onChange}>
<SelectTrigger className="w-36 sm:w-48">
<SelectValue placeholder="Select quarter" />
</SelectTrigger>
<SelectContent>
{quarters.map((q: any) => (
<SelectItem key={q.id} value={q.id}>
{q.label} {q.is_current ? '(Current)' : ''}
</SelectItem>
))}
</SelectContent>
</Select>
)
}Repobility · severity-and-effort ranking · https://repobility.com
RockCard function · typescript · L16-L92 (77 LOC)src/components/rock-card.tsx
export function RockCard({ rock, groupId, readOnly = false }: RockCardProps) {
const supabase = createClient()
const totalMilestones = rock.milestones?.length || 0
const doneMilestones = rock.milestones?.filter((m: any) => m.status === 'done').length || 0
async function toggleStatus() {
const newStatus = rock.status === 'on_track' ? 'off_track' : 'on_track'
await supabase
.from('rocks')
.update({ status: newStatus })
.eq('id', rock.id)
mutate(`rocks-${groupId}-${rock.quarter_id}`)
}
return (
<Card className="card-hover animate-fade-in">
<CardHeader className="pb-2">
<div className="flex items-start justify-between">
<Link href={`/groups/${groupId}/rocks/${rock.id}`} className="hover:underline">
<CardTitle className="text-base">{rock.title}</CardTitle>
</Link>
{!readOnly && (
<Button
variant="ghost"
size="sm"
onClick={toggleStatustoggleStatus function · typescript · L22-L29 (8 LOC)src/components/rock-card.tsx
async function toggleStatus() {
const newStatus = rock.status === 'on_track' ? 'off_track' : 'on_track'
await supabase
.from('rocks')
.update({ status: newStatus })
.eq('id', rock.id)
mutate(`rocks-${groupId}-${rock.quarter_id}`)
}RockCompletionDropdown function · typescript · L17-L118 (102 LOC)src/components/rock-completion-dropdown.tsx
export function RockCompletionDropdown({ rock, groupId }: RockCompletionDropdownProps) {
const supabase = createClient()
const { data: quarters } = useQuarters()
const [rollForwardOpen, setRollForwardOpen] = useState(false)
const [targetQuarterId, setTargetQuarterId] = useState('')
const [rollResult, setRollResult] = useState<string | null>(null)
const futureQuarters = quarters?.filter((q: any) => q.id !== rock.quarter_id) || []
async function handleCompletionChange(value: string) {
if (value === 'rolled_forward') {
setRollForwardOpen(true)
return
}
await supabase.from('rocks').update({ completion: value }).eq('id', rock.id)
mutate(`rocks-${groupId}-${rock.quarter_id}`)
mutate(`rock-${rock.id}`)
}
async function handleRollForward() {
if (!targetQuarterId) return
const { data, error } = await supabase.rpc('roll_forward_rock', {
p_rock_id: rock.id,
p_new_quarter_id: targetQuarterId,
})
if (error) {
handleCompletionChange function · typescript · L26-L35 (10 LOC)src/components/rock-completion-dropdown.tsx
async function handleCompletionChange(value: string) {
if (value === 'rolled_forward') {
setRollForwardOpen(true)
return
}
await supabase.from('rocks').update({ completion: value }).eq('id', rock.id)
mutate(`rocks-${groupId}-${rock.quarter_id}`)
mutate(`rock-${rock.id}`)
}handleRollForward function · typescript · L37-L54 (18 LOC)src/components/rock-completion-dropdown.tsx
async function handleRollForward() {
if (!targetQuarterId) return
const { data, error } = await supabase.rpc('roll_forward_rock', {
p_rock_id: rock.id,
p_new_quarter_id: targetQuarterId,
})
if (error) {
alert('Error rolling forward: ' + error.message)
return
}
setRollResult(data)
mutate(`rocks-${groupId}-${rock.quarter_id}`)
mutate(`rocks-${groupId}-${targetQuarterId}`)
mutate(`rock-${rock.id}`)
}CampaignComparisonChart function · typescript · L25-L48 (24 LOC)src/components/scorecard/campaign-comparison-chart.tsx
export function CampaignComparisonChart({ campaigns }: CampaignComparisonChartProps) {
if (campaigns.length === 0) return null
return (
<div className="space-y-2">
<p className="text-sm font-medium">Campaign Comparison</p>
<div className="h-72">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={campaigns} margin={{ top: 5, right: 20, left: 10, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" className="opacity-30" />
<XAxis dataKey="name" tick={{ fontSize: 11 }} angle={-20} textAnchor="end" height={60} />
<YAxis tick={{ fontSize: 12 }} />
<Tooltip contentStyle={{ fontSize: 12 }} />
<Legend wrapperStyle={{ fontSize: 12 }} />
<Bar dataKey="outreach" fill={COLORS.outreach} name="Outreach" />
<Bar dataKey="connects" fill={COLORS.connects} name="Connects" />
<Bar dataKey="meetings" fill={COLORS.meetings} name="Meetings" />
<Bar dCampaignGrid function · typescript · L23-L185 (163 LOC)src/components/scorecard/campaign-grid.tsx
export function CampaignGrid({ campaignId, groupId, weekEndings, readOnly = false }: CampaignGridProps) {
const { user } = useUser()
const supabase = createClient()
const { data: weeklyData } = useCampaignData(campaignId, weekEndings)
const { data: metrics } = useCampaignMetrics(groupId)
const [editingCell, setEditingCell] = useState<EditingCell | null>(null)
const [editValue, setEditValue] = useState('')
const inputRef = useRef<HTMLInputElement>(null)
useEffect(() => {
if (editingCell && inputRef.current) {
inputRef.current.focus()
inputRef.current.select()
}
}, [editingCell])
// Build lookup: weekEnding -> JSONB data object
const dataMap = new Map<string, { id: string; data: Record<string, any> }>()
weeklyData?.forEach((wd: any) => {
dataMap.set(wd.week_ending, { id: wd.id, data: wd.data || {} })
})
const saveCell = useCallback(async (metricKey: string, weekEnding: string, rawValue: string) => {
if (!user) return
const nuCellEntryPopover function · typescript · L27-L180 (154 LOC)src/components/scorecard/cell-entry-popover.tsx
export function CellEntryPopover({
measureId,
measureName,
dataType,
weekEnding,
groupId,
weekEndings,
members,
userEntryMap,
aggregateValue,
readOnly = false,
children,
}: CellEntryPopoverProps) {
const { user } = useUser()
const supabase = createClient()
const [open, setOpen] = useState(false)
const [localValue, setLocalValue] = useState('')
const inputRef = useRef<HTMLInputElement>(null)
// When popover opens, set local value to current user's entry
useEffect(() => {
if (open && user) {
const entry = userEntryMap.get(`${measureId}-${weekEnding}-${user.id}`)
setLocalValue(entry?.value != null ? String(entry.value) : '')
}
}, [open, user, measureId, weekEnding, userEntryMap])
// Auto-focus input when popover opens
useEffect(() => {
if (open && inputRef.current) {
const t = setTimeout(() => inputRef.current?.focus(), 50)
return () => clearTimeout(t)
}
}, [open])
// Compute running total from all meGenerated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
CreateCampaignDialog function · typescript · L23-L88 (66 LOC)src/components/scorecard/create-campaign-dialog.tsx
export function CreateCampaignDialog({ open, onOpenChange, groupId }: CreateCampaignDialogProps) {
const supabase = createClient()
const [name, setName] = useState('')
const [leadsCount, setLeadsCount] = useState('')
const [saving, setSaving] = useState(false)
const handleCreate = async () => {
if (!name.trim()) return
setSaving(true)
const leads = leadsCount ? parseInt(leadsCount, 10) : null
await supabase.from('campaigns').insert({
group_id: groupId,
name: name.trim(),
leads_count_total: isNaN(leads as number) ? null : leads,
status: 'active',
})
setSaving(false)
setName('')
setLeadsCount('')
onOpenChange(false)
mutate(`campaigns-${groupId}`)
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>New Campaign</DialogTitle>
<DialogDescription>Add a new campaign to track outreach metrics.</DialogDescription>
</DetailLineItemsPanel function · typescript · L32-L228 (197 LOC)src/components/scorecard/detail-line-items-panel.tsx
export function DetailLineItemsPanel({
open,
onOpenChange,
entryId,
measureName,
entryValue,
dataType,
weekEnding,
readOnly = false,
}: DetailLineItemsPanelProps) {
const supabase = createClient()
const { data: details, isLoading } = useEntryDetails(entryId)
const [newName, setNewName] = useState('')
const [newValue, setNewValue] = useState('')
const [newNotes, setNewNotes] = useState('')
// Calculate sum of line items
const lineItemSum = details?.reduce((sum: number, d: any) => sum + (Number(d.line_value) || 0), 0) || 0
const hasMismatch = entryValue != null && details && details.length > 0 && Math.abs(lineItemSum - entryValue) > 0.01
const addLineItem = useCallback(async () => {
if (!entryId || !newName.trim()) return
const value = newValue ? parseFloat(newValue.replace(/[$,]/g, '')) : null
await supabase.from('scorecard_entry_details').insert({
entry_id: entryId,
line_name: newName.trim(),
line_value: isNaN(value as EntryStatusBadge function · typescript · L11-L22 (12 LOC)src/components/scorecard/entry-status-badge.tsx
export function EntryStatusBadge({ userName, hasUpdated }: EntryStatusBadgeProps) {
return (
<div className="flex items-center gap-2 text-sm">
{hasUpdated ? (
<CheckCircle2 className="h-4 w-4 text-green-500 shrink-0" />
) : (
<XCircle className="h-4 w-4 text-red-400 shrink-0" />
)}
<span className={cn(!hasUpdated && 'text-muted-foreground')}>{userName}</span>
</div>
)
}GoalEditor function · typescript · L31-L149 (119 LOC)src/components/scorecard/goal-editor.tsx
export function GoalEditor({
open,
onOpenChange,
goalId,
measureId,
measureName,
dataType,
quarter,
currentValue,
groupId,
}: GoalEditorProps) {
const { user } = useUser()
const supabase = createClient()
const [newValue, setNewValue] = useState('')
const [reason, setReason] = useState('')
const [saving, setSaving] = useState(false)
const handleSave = useCallback(async () => {
if (!user) return
const parsed = parseInputValue(newValue, dataType)
if (parsed === null) return
setSaving(true)
if (goalId) {
// Update existing goal (trigger auto-logs the change)
await supabase
.from('scorecard_goals')
.update({ goal_value: parsed })
.eq('id', goalId)
// Manually insert reason into change log if provided
if (reason.trim()) {
// Get the most recent log entry for this goal to add reason
const { data: logs } = await supabase
.from('goal_change_log')
.select('id'MeetingScorecardReview function · typescript · L26-L170 (145 LOC)src/components/scorecard/meeting-scorecard-review.tsx
export function MeetingScorecardReview({ groupId, meetingDate, attendees }: MeetingScorecardReviewProps) {
const { user } = useUser()
const supabase = createClient()
const quarterLabel = getCurrentQuarterLabel()
const { data: settings } = useScorecardSettings(groupId)
const weekEndingDay = settings?.week_ending_day || 'friday'
// Determine the relevant week ending for this meeting
const meetingWeekEnding = useMemo(() => {
const d = new Date(meetingDate + 'T00:00:00')
return formatDate(getWeekEnding(d, weekEndingDay))
}, [meetingDate, weekEndingDay])
const weekEndings = useMemo(() => [meetingWeekEnding], [meetingWeekEnding])
const { data: template } = useScorecardTemplate(groupId)
const { data: entries } = useScorecardEntries(groupId, weekEndings)
const { data: goals } = useScorecardGoals(groupId, quarterLabel)
// Calculate entry status per attendee
const entryStatuses = useMemo(() => {
if (!template || !entries) return []
// Get all nonProgressBar function · typescript · L13-L43 (31 LOC)src/components/scorecard/progress-bar.tsx
export function ProgressBar({ label, actual, goal, dataType }: ProgressBarProps) {
const pct = goal > 0 ? Math.min((actual / goal) * 100, 100) : 0
const overGoal = goal > 0 && actual > goal
return (
<div className="space-y-1.5">
<div className="flex items-center justify-between text-sm">
<span className="font-medium truncate">{label}</span>
<span className="text-muted-foreground shrink-0 ml-2">
{formatValue(actual, dataType)} / {formatValue(goal, dataType)}
</span>
</div>
<div className="h-3 rounded-full bg-muted overflow-hidden">
<div
className={cn(
'h-full rounded-full transition-all duration-500',
overGoal ? 'bg-green-500' :
pct >= 70 ? 'bg-green-500' :
pct >= 40 ? 'bg-yellow-500' :
'bg-red-500'
)}
style={{ width: `${pct}%` }}
/>
</div>
<div className="flex justify-between text-xs text-muted-foreground">
RollupRow function · typescript · L21-L104 (84 LOC)src/components/scorecard/rollup-row.tsx
export function RollupRow({
label,
level,
weekEndings,
weekTotals,
goalTotal,
dataType,
children,
defaultExpanded = false,
}: RollupRowProps) {
const [expanded, setExpanded] = useState(defaultExpanded)
let grandTotal = 0
weekEndings.forEach((week) => {
grandTotal += weekTotals.get(week) || 0
})
const pctToGoal = goalTotal ? percentToGoal(grandTotal, goalTotal) : null
return (
<>
<tr
className={cn(
'border-t cursor-pointer transition-colors',
level === 'company' ? 'bg-primary/10 font-semibold' : 'bg-muted/40 font-medium'
)}
onClick={() => setExpanded(!expanded)}
>
{/* Label with expand/collapse icon */}
<td className="sticky left-0 z-10 px-3 py-2 text-left whitespace-nowrap"
style={{ backgroundColor: 'inherit' }}
>
<div className="flex items-center gap-1.5">
{expanded
? <ChevronDown className="h-4 w-4 shrink-0 text-muted-forcomputeRollupTotals function · typescript · L107-L122 (16 LOC)src/components/scorecard/rollup-row.tsx
export function computeRollupTotals(
measureIds: string[],
weekEndings: string[],
entryMap: Map<string, any>
): Map<string, number> {
const totals = new Map<string, number>()
weekEndings.forEach((week) => {
let sum = 0
measureIds.forEach((mid) => {
const entry = entryMap.get(`${mid}-${week}`)
if (entry?.value != null) sum += Number(entry.value)
})
totals.set(week, sum)
})
return totals
}Repobility — the code-quality scanner for AI-generated software · https://repobility.com
computeGoalTotal function · typescript · L125-L134 (10 LOC)src/components/scorecard/rollup-row.tsx
export function computeGoalTotal(
measureIds: string[],
goalMap: Map<string, number>
): number {
let total = 0
measureIds.forEach((mid) => {
total += goalMap.get(mid) || 0
})
return total
}ScorecardGrid function · typescript · L24-L183 (160 LOC)src/components/scorecard/scorecard-grid.tsx
export function ScorecardGrid({
template,
entries,
goals,
weekEndings,
groupId,
quarter,
readOnly = false,
onCellClick,
onCreateIssue,
onGoalEdit,
}: ScorecardGridProps) {
const { data: members } = useGroupMembers(groupId)
// Build per-user entry map (for popover individual values)
const userEntryMap = useMemo(() => {
const map = new Map<string, any>()
entries?.forEach((e: any) => {
map.set(`${e.measure_id}-${e.week_ending}-${e.user_id}`, e)
})
return map
}, [entries])
// Build aggregate map (for grid cell display - sum per measure+week)
// Also compute calculated measure values from their formulas
const aggregateMap = useMemo(() => {
const map = new Map<string, number>()
entries?.forEach((e: any) => {
const key = `${e.measure_id}-${e.week_ending}`
map.set(key, (map.get(key) || 0) + Number(e.value || 0))
})
// Compute calculated measures (e.g., sum of source measures)
template?.scorecard_sectionsScorecardSection function · typescript · L202-L338 (137 LOC)src/components/scorecard/scorecard-grid.tsx
function ScorecardSection({
section,
weekEndings,
rollupEntryMap,
aggregateMap,
userEntryMap,
goalMap,
goalIdMap,
members,
groupId,
readOnly,
getMeasureTotal,
onDetailClick,
onCreateIssue,
onGoalEdit,
}: ScorecardSectionProps) {
const measures = section.scorecard_measures || []
// Compute team-level rollup for this section
const sectionMeasureIds = measures.filter((m: any) => !m.is_calculated).map((m: any) => m.id)
const sectionWeekTotals = computeRollupTotals(sectionMeasureIds, weekEndings, rollupEntryMap)
const sectionGoalTotal = computeGoalTotal(sectionMeasureIds, goalMap)
return (
<>
<RollupRow
label={section.name}
level="team"
weekEndings={weekEndings}
weekTotals={sectionWeekTotals}
goalTotal={sectionGoalTotal}
dataType="currency"
defaultExpanded={true}
>
{measures.map((measure: any) => {
const goal = goalMap.get(measure.id)
const total = ge