Files
OmniRoute/tests/unit
Diego Rodrigues de Sa e Souza d86ea99713 feat(radar): referral links — free-credits tab + default provider link (#9697)
* feat(radar): client-side schema + accessor for referral links (D28)

Server already publishes a signed `referrals` section on the Radar feed
({fixed, campaigns}); this adds the client mirror: RadarFeedSchema gains a
`.default()`-backed `referrals` field (old cached feeds without it stay
valid) with https-only url validation, and src/lib/radar/index.ts exposes
getRadarReferrals()/getDefaultReferralFor() (never throw: flag off, no
cache, or a corrupt/old payload all resolve to the empty shape). The
provider-default lookup itself lives in a new DB-free src/lib/radar/
referrals.ts so it stays safe to import from a "use client" component.

* feat(radar): add GET /api/radar/referrals route (D28)

Local-only route mirroring the /api/radar/catalog gate order: RADAR_ENABLED
off => 404 before any auth check (byte-identical flag-off inertia),
unauthenticated => 401, otherwise 200 with {fixed, campaigns, tier} read
straight from the local cache. Never proxies the private feed server.

* feat(dashboard): add "free credits" tab to the Radar page (D28)

Reuses the existing /dashboard/radar page instead of a new route (less
routing/i18n surface): a second tab lists fixed referral links (grouped by
provider, with requiredAction + an external-link button) and temporary
campaigns (with validUntil). When campaigns is empty and the served tier is
community, shows a soft upsell note — never gates the fixed links list,
which stays fully populated on every tier. Adds 10 new radarPage i18n keys
(English fallback) to all 43 locale files to avoid dropping i18n-ui-coverage
below threshold.

* feat(providers): use Radar default referral link on the provider name (D28)

ProviderPageHeader already linked the provider name to providerInfo.website
with a precedent for a monetized link (the Kimi partner-link note); this
lets a Radar default referral override that URL, reusing the exact same
discreet note instead of a new visual treatment.

Loose coupling: resolveProviderHeaderLink() in providerPageUtils.ts is a
pure function with no @/lib/radar or @/lib/db/* import (asserted by the new
test), so the providers dashboard never depends on the DB-touching Radar
module to render. ProviderDetailPageClient (a "use client" component) is
the only place that fetches Radar data, via the local /api/radar/referrals
route (same pattern the Radar page itself uses) and the DB-free
findDefaultReferral() helper. With RADAR_ENABLED off, no cache, or no
default referral for the provider, the header renders byte-identical to
before this feature existed.

* docs(radar): document referral links / free credits (D28)

Adds a "Referral links (free credits)" section covering the referrals feed
shape, the getRadarReferrals()/getDefaultReferralFor() accessors, the new
GET /api/radar/referrals route, the Radar page's "Free credits" tab, and
the loosely-coupled referral link on the provider-name header. Also
corrects the local-routes count (four -> five) now that /api/radar/
referrals exists.

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-08-07 10:17:46 -03:00
..
2026-07-02 10:47:13 -03:00
2026-06-30 06:54:29 -03:00
2026-06-17 19:26:32 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-06-09 22:57:12 -03:00
2026-06-30 06:54:29 -03:00
2026-07-29 15:18:55 -03:00
2026-06-29 08:40:06 -03:00
2026-07-02 10:47:13 -03:00
2026-06-25 13:17:40 -03:00
2026-06-29 16:51:03 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-06-28 06:58:29 -03:00
2026-06-27 09:07:12 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-05-29 12:44:29 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-06-23 17:06:18 -03:00
2026-06-13 17:27:40 -03:00
2026-07-13 09:12:40 -03:00
2026-07-13 09:12:40 -03:00
2026-07-04 13:00:30 -03:00
2026-07-02 10:47:13 -03:00
2026-05-29 12:44:29 -03:00
2026-06-29 16:51:03 -03:00
2026-05-29 12:44:29 -03:00
2026-06-27 09:07:12 -03:00
2026-07-29 15:18:55 -03:00
2026-05-24 18:05:58 -03:00
2026-06-17 19:26:32 -03:00
2026-05-26 23:51:47 -03:00
2026-07-13 09:12:40 -03:00
2026-07-13 09:12:40 -03:00
2026-06-22 03:17:02 -03:00
2026-06-29 08:40:06 -03:00
2026-07-13 09:12:40 -03:00
2026-06-13 17:27:40 -03:00
2026-06-16 01:00:40 -03:00
2026-07-07 13:14:06 -03:00
2026-06-29 08:40:06 -03:00
2026-06-21 08:56:51 -03:00
2026-05-26 23:51:47 -03:00
2026-06-21 08:56:51 -03:00
2026-06-23 03:08:29 -03:00
2026-05-29 12:44:29 -03:00
2026-06-29 08:40:06 -03:00
2026-05-26 23:51:47 -03:00
2026-07-13 09:12:40 -03:00
2026-05-26 23:51:47 -03:00
2026-05-29 12:44:29 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-05-10 00:55:06 -03:00
2026-05-26 23:51:47 -03:00
2026-06-13 17:27:40 -03:00
2026-07-29 15:18:55 -03:00
2026-06-29 08:40:06 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-06-27 09:07:12 -03:00
2026-06-29 16:51:03 -03:00
2026-04-30 01:27:03 -03:00
2026-04-30 01:27:03 -03:00
2026-07-07 13:14:06 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-06-23 17:06:18 -03:00
2026-07-29 15:18:55 -03:00
2026-07-04 13:00:30 -03:00
2026-07-04 13:00:30 -03:00
2026-06-26 02:51:06 -03:00
2026-07-29 15:18:55 -03:00
2026-07-07 13:14:06 -03:00
2026-06-29 16:51:03 -03:00
2026-05-26 23:51:47 -03:00
2026-04-30 14:08:50 -03:00
2026-06-29 16:51:03 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-06-17 02:43:21 -03:00
2026-07-29 15:18:55 -03:00
2026-05-26 23:51:47 -03:00
2026-07-06 02:25:17 -03:00
2026-06-15 03:32:11 -03:00
2026-07-29 15:18:55 -03:00
2026-05-26 23:51:47 -03:00
2026-07-13 09:12:40 -03:00
2026-07-29 15:18:55 -03:00
2026-07-07 13:14:06 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-07-07 13:14:06 -03:00
2026-07-07 13:14:06 -03:00
2026-07-29 15:18:55 -03:00
2026-06-20 14:55:24 -03:00
2026-06-22 03:17:02 -03:00
2026-07-13 09:12:40 -03:00
2026-07-04 13:00:30 -03:00
2026-06-26 02:51:06 -03:00
2026-07-04 13:00:30 -03:00
2026-06-27 09:07:12 -03:00
2026-06-19 06:49:01 -03:00
2026-06-20 07:09:43 -03:00
2026-07-07 13:14:06 -03:00
2026-07-07 13:14:06 -03:00
2026-04-30 01:27:03 -03:00
2026-06-22 03:17:02 -03:00
2026-07-29 15:18:55 -03:00
2026-07-07 13:14:06 -03:00
2026-07-07 13:14:06 -03:00
2026-06-19 06:49:01 -03:00
2026-06-12 23:49:22 -03:00
2026-06-12 23:49:22 -03:00
2026-06-11 18:52:29 -03:00
2026-06-29 08:40:06 -03:00
2026-07-29 15:18:55 -03:00
2026-06-30 06:54:29 -03:00
2026-07-04 13:00:30 -03:00
2026-07-29 15:18:55 -03:00
2026-06-30 06:54:29 -03:00
2026-06-29 16:51:03 -03:00
2026-07-04 13:00:30 -03:00
2026-07-29 15:18:55 -03:00
2026-06-29 08:40:06 -03:00
2026-07-07 13:14:06 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-05-23 01:46:59 -03:00
2026-05-23 01:46:59 -03:00
2026-06-09 15:56:24 -03:00
2026-07-29 15:18:55 -03:00
2026-05-29 19:54:00 -03:00
2026-06-29 08:40:06 -03:00
2026-07-07 13:14:06 -03:00
2026-06-17 19:26:32 -03:00
2026-06-13 17:27:40 -03:00
2026-07-07 13:14:06 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-06-30 06:54:29 -03:00
2026-07-04 13:00:30 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-05-24 18:05:58 -03:00
2026-06-30 06:54:29 -03:00
2026-06-29 08:40:06 -03:00
2026-07-29 15:18:55 -03:00
2026-04-28 20:46:25 -03:00
2026-07-13 09:12:40 -03:00
2026-07-29 15:18:55 -03:00
2026-07-13 09:12:40 -03:00
2026-07-13 09:12:40 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-05-29 12:44:29 -03:00
2026-06-29 16:51:03 -03:00
2026-06-23 03:08:29 -03:00
2026-05-26 23:51:47 -03:00
2026-06-30 06:54:29 -03:00
2026-06-12 23:49:22 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-07-02 10:47:13 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-07-29 15:18:55 -03:00
2026-06-16 01:00:40 -03:00
2026-05-26 23:51:47 -03:00
2026-06-04 20:05:38 -03:00
2026-06-12 23:49:22 -03:00
2026-07-07 13:14:06 -03:00
2026-06-29 16:51:03 -03:00
2026-06-29 08:40:06 -03:00
2026-05-29 12:44:29 -03:00
2026-07-07 13:14:06 -03:00
2026-06-20 07:09:43 -03:00
2026-06-19 06:49:01 -03:00
2026-06-07 07:20:02 -03:00
2026-07-29 15:18:55 -03:00
2026-06-11 04:01:24 -03:00
2026-06-29 08:40:06 -03:00
2026-05-24 18:05:58 -03:00
2026-05-26 23:51:47 -03:00
2026-06-12 23:49:22 -03:00
2026-06-16 01:00:40 -03:00
2026-06-12 23:49:22 -03:00
2026-06-27 09:07:12 -03:00
2026-07-29 15:18:55 -03:00
2026-07-07 13:14:06 -03:00
2026-07-04 13:00:30 -03:00
2026-07-04 13:00:30 -03:00
2026-06-16 01:00:40 -03:00
2026-07-07 13:14:06 -03:00