104 lines
4.0 KiB
TypeScript
104 lines
4.0 KiB
TypeScript
/** Service for coupon-related API calls */
|
|
import { supabase } from '@/supabase'
|
|
import type { Coupon } from '@/types'
|
|
|
|
export interface CouponFilters {
|
|
category?: string
|
|
is_active?: boolean
|
|
active_only?: boolean
|
|
}
|
|
|
|
export const couponsService = {
|
|
/** Get all coupons with optional filters */
|
|
async getAllCoupons(filters?: CouponFilters): Promise<Coupon[]> {
|
|
let query = supabase.from('coupons').select('*, business:businesses(*)')
|
|
|
|
if (filters?.category) query = query.eq('category', filters.category)
|
|
if (filters?.is_active !== undefined) query = query.eq('is_active', filters.is_active)
|
|
if (filters?.active_only !== undefined && filters.active_only) {
|
|
query = query.eq('is_active', true)
|
|
}
|
|
|
|
const { data, error } = await query
|
|
if (error) throw new Error(error.message)
|
|
return data as Coupon[]
|
|
},
|
|
|
|
/** Get a single coupon by ID */
|
|
async getCouponById(id: string): Promise<Coupon> {
|
|
const { data, error } = await supabase.from('coupons').select('*, business:businesses(*)').eq('id', id).single()
|
|
if (error) throw new Error(error.message)
|
|
return data as Coupon
|
|
},
|
|
|
|
/** Create a new coupon */
|
|
async createCoupon(coupon: Omit<Coupon, 'id' | 'created_at' | 'updated_at'>): Promise<Coupon> {
|
|
// Prevent sending nested business properties over insert
|
|
const { business, business_name, business_address, business_phone, created_at, updated_at, id, ...payload } = coupon as any
|
|
const { data, error } = await supabase.from('coupons').insert([payload]).select().single()
|
|
if (error) throw new Error(error.message)
|
|
return data as Coupon
|
|
},
|
|
|
|
/** Update an existing coupon */
|
|
async updateCoupon(id: string, coupon: Partial<Coupon>): Promise<Coupon> {
|
|
const { business, business_name, business_address, business_phone, created_at, updated_at, id: _id, ...payload } = coupon as any
|
|
const { data, error } = await supabase.from('coupons').update(payload).eq('id', id).select().single()
|
|
if (error) throw new Error(error.message)
|
|
return data as Coupon
|
|
},
|
|
|
|
/** Delete a coupon */
|
|
async deleteCoupon(id: string): Promise<void> {
|
|
const { error } = await supabase.from('coupons').delete().eq('id', id)
|
|
if (error) throw new Error(error.message)
|
|
},
|
|
|
|
/** Claim a coupon */
|
|
async claimCoupon(id: string): Promise<any> {
|
|
const { data: userData, error: userError } = await supabase.auth.getUser()
|
|
if (userError || !userData?.user) throw new Error('User not logged in.')
|
|
|
|
const claimPayload = {
|
|
user_id: userData.user.id,
|
|
coupon_id: id,
|
|
status: 'claimed',
|
|
redemption_code: Math.random().toString(36).substring(2, 8).toUpperCase()
|
|
}
|
|
|
|
const { data, error } = await supabase.from('user_coupons').insert([claimPayload]).select().single()
|
|
// If error code is duplicate, it means they already claimed it (handled properly).
|
|
if (error) throw error
|
|
|
|
return data
|
|
},
|
|
|
|
/** Get current user's claimed coupons */
|
|
async getMyCoupons(): Promise<any[]> {
|
|
const { data: userData } = await supabase.auth.getUser()
|
|
if (!userData || !userData.user) return []
|
|
|
|
const { data, error } = await supabase
|
|
.from('user_coupons')
|
|
.select('*, coupon:coupons(*, business:businesses(*))')
|
|
.eq('user_id', userData.user.id)
|
|
|
|
if (error) throw new Error(error.message)
|
|
return data || []
|
|
},
|
|
|
|
/** Validate a coupon by code (merchants/drivers only) */
|
|
async validateCoupon(code: string): Promise<any> {
|
|
const { data, error } = await supabase
|
|
.from('user_coupons')
|
|
.update({ status: 'redeemed', redeemed_at: new Date().toISOString() })
|
|
.eq('redemption_code', code)
|
|
.select()
|
|
.single()
|
|
|
|
if (error) throw new Error(error.message)
|
|
if (!data) throw new Error('Invalid code or already redeemed.')
|
|
return data
|
|
}
|
|
}
|