This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

add stats

+88 -30
+31 -5
app/src/app/api/bluesky/stats/route.ts
··· 62 62 flushesPerDay = parseFloat(((totalCount || 0) / activeDaysCount).toFixed(1)); 63 63 } 64 64 65 - // 3. Get top flushers (leaderboard) 65 + // 3. Get top flushers (leaderboard) - excluding test accounts 66 66 const { data: leaderboardData, error: leaderboardError } = await supabase 67 67 .from('flushing_records') 68 - .select('did') 68 + .select('did, handle') 69 69 .order('created_at', { ascending: false }); 70 70 71 71 if (leaderboardError) { ··· 74 74 75 75 // Count flushes by DID 76 76 const didCounts = new Map<string, number>(); 77 + 78 + // Special count for the plumber 79 + let plumberFlushCount = 0; 80 + 81 + // List of DIDs to exclude from leaderboard 82 + const excludedDids = [ 83 + 'did:plc:fouf3svmcxzn6bpiw3lgwz22', // plumber.flushing.im 84 + 'did:plc:fnhrjbkwjiw6iyxxg2o3rljw' // testing.dame.is 85 + ]; 86 + 87 + // List of handles to exclude from leaderboard (as fallback) 88 + const excludedHandles = [ 89 + 'plumber.flushing.im', 90 + 'testing.dame.is' 91 + ]; 92 + 77 93 leaderboardData?.forEach(entry => { 78 - didCounts.set(entry.did, (didCounts.get(entry.did) || 0) + 1); 94 + // Check if this is the plumber or test account 95 + if (entry.did === 'did:plc:fouf3svmcxzn6bpiw3lgwz22' || entry.handle === 'plumber.flushing.im') { 96 + plumberFlushCount++; 97 + } 98 + // Only count towards leaderboard if not an excluded account 99 + else if (!excludedDids.includes(entry.did) && 100 + !(entry.handle && excludedHandles.includes(entry.handle))) { 101 + didCounts.set(entry.did, (didCounts.get(entry.did) || 0) + 1); 102 + } 79 103 }); 80 104 81 105 // Convert to array and sort by count ··· 89 113 totalCount, 90 114 flushesPerDay, 91 115 chartData: chartData.slice(-30), // Last 30 days 92 - leaderboard 116 + leaderboard, 117 + plumberFlushCount 93 118 }); 94 119 } else { 95 120 // If no Supabase credentials, return mock data ··· 97 122 totalCount: 42, 98 123 flushesPerDay: 3.5, 99 124 chartData: generateMockChartData(), 100 - leaderboard: generateMockLeaderboard() 125 + leaderboard: generateMockLeaderboard(), 126 + plumberFlushCount: 15 101 127 }); 102 128 } 103 129 } catch (error: any) {
+3 -24
app/src/app/page.tsx
··· 176 176 setText('is '); 177 177 setSuccess('Your flushing status has been updated!'); 178 178 179 - // Create a temporary entry to display immediately 180 - if (result && result.uri && result.cid) { 181 - const tempEntry: FlushingEntry = { 182 - id: `temp-${Date.now()}`, // Create a temporary ID 183 - uri: result.uri, 184 - cid: result.cid, 185 - authorDid: did, 186 - authorHandle: handle || 'unknown', 187 - text: formattedText, 188 - emoji: selectedEmoji, 189 - createdAt: new Date().toISOString() 190 - }; 191 - 192 - console.log('Adding temporary entry to feed for immediate display:', tempEntry); 193 - 194 - // Add the temporary entry to the top of the feed 195 - setEntries(prevEntries => [tempEntry, ...prevEntries]); 196 - 197 - // Also mark it as a new entry for animation 198 - setNewEntryIds(new Set([tempEntry.id])); 199 - } 200 - 201 179 // Close status form after successful submission 202 180 setTimeout(() => { 203 181 setStatusOpen(false); 204 182 }, 2000); 205 183 206 - // Still refresh the feed after a delay to get the actual database entry 184 + // Refresh the feed after a delay to get the newly created entry 207 185 setTimeout(() => { 186 + console.log('Refreshing feed to show new entry...'); 208 187 fetchLatestEntries(true); 209 - }, 3000); 188 + }, 2500); 210 189 } catch (err: any) { 211 190 console.error('Failed to update status:', err); 212 191 setStatusError(`Failed to update status: ${err.message || 'Unknown error'}`);
+24
app/src/app/stats/page.tsx
··· 11 11 flushesPerDay: number; 12 12 chartData: { date: string; count: number }[]; 13 13 leaderboard: { did: string; count: number; handle?: string }[]; 14 + plumberFlushCount: number; 14 15 } 15 16 16 17 export default function StatsPage() { ··· 158 159 <div className={styles.statCard}> 159 160 <div className={styles.statValue}>{statsData.flushesPerDay}</div> 160 161 <div className={styles.statLabel}>Flushes Per Active Day</div> 162 + </div> 163 + <div className={styles.statCard}> 164 + <div className={styles.statValue}> 165 + <a 166 + href="https://bsky.app/profile/plumber.flushing.im" 167 + target="_blank" 168 + rel="noopener noreferrer" 169 + className={styles.plumberLink} 170 + title="Visit the plumber's Bluesky profile" 171 + > 172 + 🪠 {statsData.plumberFlushCount} 173 + </a> 174 + </div> 175 + <div className={styles.statLabel}> 176 + <a 177 + href="https://bsky.app/profile/plumber.flushing.im" 178 + target="_blank" 179 + rel="noopener noreferrer" 180 + className={styles.plumberLink} 181 + > 182 + Emergency Plumber Visits 183 + </a> 184 + </div> 161 185 </div> 162 186 </div> 163 187 </section>
+30 -1
app/src/app/stats/stats.module.css
··· 235 235 /* Stats Grid */ 236 236 .statsGrid { 237 237 display: grid; 238 - grid-template-columns: repeat(2, 1fr); 238 + grid-template-columns: repeat(3, 1fr); 239 239 gap: 1.5rem; 240 240 } 241 241 ··· 263 263 .statLabel { 264 264 color: var(--text-color); 265 265 font-size: 1.1rem; 266 + } 267 + 268 + /* Plumber link styles */ 269 + .plumberLink { 270 + color: inherit; 271 + text-decoration: none; 272 + display: inline-block; 273 + transition: transform 0.2s, color 0.2s; 274 + position: relative; 275 + } 276 + 277 + .plumberLink:hover { 278 + color: #e84142; /* A reddish color for plumber branding */ 279 + transform: scale(1.05); 280 + } 281 + 282 + .plumberLink:after { 283 + content: ''; 284 + position: absolute; 285 + width: 0; 286 + height: 2px; 287 + bottom: -2px; 288 + left: 0; 289 + background-color: #e84142; 290 + transition: width 0.3s; 291 + } 292 + 293 + .plumberLink:hover:after { 294 + width: 100%; 266 295 } 267 296 268 297 /* Chart Styles */