Files
SIB/frontend/src/services/couponsService.ts

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
}
}