···169169 // Fallback: calculate from available ratings
170170 return calculateWeightedScore(review)
171171}
172172+173173+/**
174174+ * Format notification time window for a specific stage
175175+ * Returns formatted text like "in 1.5-4 hours" or specific times
176176+ */
177177+export function formatNotificationWindow(createdAt: string, stage: 'stage2' | 'stage3'): string {
178178+ const created = new Date(createdAt)
179179+180180+ if (stage === 'stage2') {
181181+ const start = new Date(created.getTime() + 1.5 * 60 * 60 * 1000)
182182+ const end = new Date(created.getTime() + 4 * 60 * 60 * 1000)
183183+184184+ const startTime = start.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
185185+ const endTime = end.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
186186+187187+ return `${startTime} - ${endTime}`
188188+ } else {
189189+ // Stage 3: starts at 4 hours, no end time
190190+ const start = new Date(created.getTime() + 4 * 60 * 60 * 1000)
191191+ const startTime = start.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
192192+193193+ return `after ${startTime}`
194194+ }
195195+}
+8
src/utils/reviewValidation.ts
···108108109109 return { valid: true }
110110}
111111+112112+/**
113113+ * Check if review's fragrance connection can be changed
114114+ * This is allowed at ANY time, even after 24 hours when other edits are locked
115115+ */
116116+export function canChangeFragrance(_review: any): boolean {
117117+ return true // Always allowed
118118+}
+28
src/validation/cascade.ts
···19192020 return { canDelete: true }
2121}
2222+2323+/**
2424+ * Get count of fragrances owned by user that reference a house
2525+ */
2626+export function getOwnedFragranceCount(
2727+ houseUri: AtUri,
2828+ fragrances: Fragrance[],
2929+ ownerDid: string
3030+): number {
3131+ return fragrances.filter(f => {
3232+ const [, , fragDid] = (f.uri || '').split('/')
3333+ return f.house === houseUri && fragDid === ownerDid
3434+ }).length
3535+}
3636+3737+/**
3838+ * Get all fragrances owned by user that reference a house
3939+ */
4040+export function getOwnedFragrancesForHouse(
4141+ houseUri: AtUri,
4242+ fragrances: Fragrance[],
4343+ ownerDid: string
4444+): Fragrance[] {
4545+ return fragrances.filter(f => {
4646+ const [, , fragDid] = (f.uri || '').split('/')
4747+ return f.house === houseUri && fragDid === ownerDid
4848+ })
4949+}