forked from cartsnitch/cartsnitch
fix: address Chip's review — secure auth, wire TanStack Query, fix UX issues
Must-fix: - Exclude JWT token from Zustand persist (partialize) to prevent localStorage XSS exfiltration — token now lives in memory only - Wire all pages through TanStack Query hooks (usePurchases, useProduct, useProducts, usePriceHistory, useCoupons, usePriceAlerts) with proper loading skeletons and error states - Add mock interceptor in api.ts (VITE_MOCK_API=true) so mock data flows through the same fetch path — single flag to switch to live API Should-fix: - Wire theme toggle to DOM (dark class on <html>) - Fix AccountLinking form inputs (controlled with value/onChange) - Remove unused err in catch blocks (Login, Register) - Bump remaining min-h-10 touch targets to min-h-12 (48px) Build: 128KB initial JS, Recharts 498KB lazy chunk. 5/5 tests pass. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
+25
-3
@@ -1,8 +1,9 @@
|
||||
import { useState } from 'react'
|
||||
import { mockCoupons } from '../lib/mock-data.ts'
|
||||
import { useCoupons } from '../hooks/useApi.ts'
|
||||
import { StoreIcon } from '../components/StoreIcon.tsx'
|
||||
|
||||
export function Coupons() {
|
||||
const { data: coupons = [], isLoading, error } = useCoupons()
|
||||
const [copied, setCopied] = useState<string | null>(null)
|
||||
|
||||
function handleCopy(code: string, id: string) {
|
||||
@@ -17,12 +18,33 @@ export function Coupons() {
|
||||
Target: 'target',
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="animate-pulse">
|
||||
<div className="h-8 w-40 rounded bg-gray-200" />
|
||||
<div className="mt-4 space-y-3">
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div key={i} className="h-24 rounded-xl bg-gray-200" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="py-8 text-center">
|
||||
<p className="text-sm text-red-600">Failed to load coupons.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Coupons & Deals</h1>
|
||||
|
||||
<div className="mt-4 space-y-3">
|
||||
{mockCoupons.map((coupon) => {
|
||||
{coupons.map((coupon) => {
|
||||
const isExpiringSoon =
|
||||
new Date(coupon.expiresAt).getTime() - Date.now() < 7 * 24 * 60 * 60 * 1000
|
||||
|
||||
@@ -54,7 +76,7 @@ export function Coupons() {
|
||||
{coupon.code && (
|
||||
<button
|
||||
onClick={() => handleCopy(coupon.code!, coupon.id)}
|
||||
className="mt-3 flex min-h-10 w-full items-center justify-center gap-2 rounded-lg border border-dashed border-gray-300 px-4 py-2 text-sm font-mono active:bg-gray-50"
|
||||
className="mt-3 flex min-h-12 w-full items-center justify-center gap-2 rounded-lg border border-dashed border-gray-300 px-4 py-2 text-sm font-mono active:bg-gray-50"
|
||||
>
|
||||
<span className="text-gray-700">{coupon.code}</span>
|
||||
<span className="text-xs text-brand-blue">
|
||||
|
||||
Reference in New Issue
Block a user