loyalty
Reads the store's loyalty program (ways to earn points, prizes) and the signed-in customer's points balance.
import { loyalty } from '@salla.sa/twilight-theme-engine/api/loyalty';In plain words
Some stores reward customers with points (for buying, for sharing, for a birthday) and let them trade points for prizes. loyalty.get() returns the program: its name, the ways to earn and the prizes. loyalty.getPointsBalance() returns how many points the signed-in customer has.
The program is public; the balance belongs to a customer, so it needs a signed-in shopper.
Signature
loyalty.get(): Promise<Loyalty> // GET loyalty
loyalty.getOrThrow(): Promise<Loyalty> // any failure → NotFoundError
loyalty.getPointsBalance(): Promise<number> // GET balance/points → response.balance
loyalty.queries.detail() // key ['loyalty']
loyalty.queries.points() // key ['loyalty', 'points'], enabled: !!getAuthToken(), staleTime 30 s
// The Loyalty type is not exported; use Awaited<ReturnType<typeof loyalty.get>>
// { id; name; description?; image?; customer: {…} | null; points: LoyaltyPoint[]; prizes: LoyaltyPrize[]; … }Try it live
import { useQuery } from '@tanstack/react-query';
import { loyalty } from '@salla.sa/twilight-theme-engine/api/loyalty';
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';
export function LoyaltyBadge() {
const { settings } = useStore();
const { data: program } = useQuery({
...loyalty.queries.detail(),
enabled: Boolean(settings.is_loyalty_enabled), // the API refuses when the program is off
});
const { data: balance } = useQuery(loyalty.queries.points()); // idle for guests
if (!program) return null;
return (
<p className="loyalty-badge">
{program.name}
{balance != null && <span> · {balance} points</span>}
</p>
);
}
Example
import { useQuery } from '@tanstack/react-query';
import { loyalty } from '@salla.sa/twilight-theme-engine/api/loyalty';
export function PointsBalance() {
// Disabled for guests by the options themselves, and fresh for 30 seconds.
const { data: balance } = useQuery(loyalty.queries.points());
if (balance == null) return null;
return <p className="points-balance">You have {balance} points</p>;
}
How it behaves
queries.points()decidesenabledfromgetAuthToken()at the moment you call it. Build the options during render: options built once at module level stay disabled after a login.The engine cart page reads
loyalty.queries.points(), and the loyalty page loader callsloyalty.getOrThrow().getPointsBalancereadsbalancefrom the top of the response body, not fromdata.customerin the program isnullfor a guest.LoyaltyPoint,LoyaltyPrizeandLoyaltyPrizeItemare exported from@salla.sa/twilight-theme-engine/routes/loyalty.
Gotchas
On a store with loyalty switched off (
store.settings.is_loyalty_enabledisfalse),GET loyaltyanswers HTTP 400 with a "temporarily unavailable" message (demo store).getthrows,getOrThrowshows the 404 page, and a plainuseQueryretries once before failing. CheckuseStore().settings.is_loyalty_enabledfirst.Calling
getPointsBalance()directly as a guest throws (HTTP 400token_not_provided, demo store); only the query options guard against it.Loyaltyexported from@salla.sa/twilight-theme-engine/routes/loyaltyis the loyalty route module, not the program type.import type { Loyalty }from there does not describeloyalty.get().src/api/README.md calls the balance method
loyalty.getPoints(). It isgetPointsBalance().
Related
Source and docs
- Engine source:
packages/theme-engine/src/api/loyalty.ts