← back to Letsbegin
lib/sheets.ts
118 lines
/**
* Google Sheets integration for tracking project progress
* Uses the existing Google Sheets API configuration
*/
const SHEETS_WEBHOOK_URL = process.env.GOOGLE_SHEETS_WEBHOOK_URL || ''
const SHEETS_ID = process.env.GOOGLE_SHEETS_ID || ''
interface TrackingEvent {
timestamp: string
project: string
step: 'prd_generated' | 'ralph_converted' | 'ralph_started' | 'story_completed' | 'ralph_finished'
details: string
status: 'success' | 'error' | 'in_progress'
storyId?: string
duration?: number
}
export async function trackEvent(event: TrackingEvent): Promise<boolean> {
try {
// Store locally in JSON file
const fs = await import('fs')
const path = await import('path')
const trackingDir = path.join(process.cwd(), 'data')
const trackingFile = path.join(trackingDir, 'tracking.json')
if (!fs.existsSync(trackingDir)) {
fs.mkdirSync(trackingDir, { recursive: true })
}
let events: TrackingEvent[] = []
if (fs.existsSync(trackingFile)) {
events = JSON.parse(fs.readFileSync(trackingFile, 'utf8'))
}
events.push({
...event,
timestamp: event.timestamp || new Date().toISOString()
})
// Keep last 1000 events
if (events.length > 1000) {
events = events.slice(-1000)
}
fs.writeFileSync(trackingFile, JSON.stringify(events, null, 2))
// If Google Sheets webhook is configured, send there too
if (SHEETS_WEBHOOK_URL) {
await fetch(SHEETS_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
values: [[
event.timestamp,
event.project,
event.step,
event.details,
event.status,
event.storyId || '',
event.duration || ''
]]
})
})
}
return true
} catch (error) {
console.error('Failed to track event:', error)
return false
}
}
export async function getTrackingEvents(project?: string, limit = 50): Promise<TrackingEvent[]> {
try {
const fs = await import('fs')
const path = await import('path')
const trackingFile = path.join(process.cwd(), 'data', 'tracking.json')
if (!fs.existsSync(trackingFile)) {
return []
}
let events: TrackingEvent[] = JSON.parse(fs.readFileSync(trackingFile, 'utf8'))
if (project) {
events = events.filter(e => e.project === project)
}
return events.slice(-limit).reverse()
} catch (error) {
console.error('Failed to get tracking events:', error)
return []
}
}
export async function getProjectStats(project: string): Promise<{
totalPRDs: number
totalRalphRuns: number
successRate: number
lastActivity: string | null
}> {
const events = await getTrackingEvents(project, 1000)
const prdEvents = events.filter(e => e.step === 'prd_generated')
const ralphRuns = events.filter(e => e.step === 'ralph_finished')
const successfulRuns = ralphRuns.filter(e => e.status === 'success')
return {
totalPRDs: prdEvents.length,
totalRalphRuns: ralphRuns.length,
successRate: ralphRuns.length > 0 ? (successfulRuns.length / ralphRuns.length) * 100 : 0,
lastActivity: events[0]?.timestamp || null
}
}