Semih 096ca9b4a3 feat(sase): brand picker — Phase F
Pairs with sase.tr#31 (POST /internal/admin/subscriptions/:id/brands).

Admin SDK
- setSubscriptionBrands({ subscriptionId, brandIds, reason, founderId }).
- BrandSetResult type; SASE_ADMIN_ENDPOINTS list updated.

Repo
- listBrands() helper (RO).
- getUser() now returns currentPlanBrandCount + brandIds for the picker.

Route
- POST /api/sase/subscriptions/[subId]/brands — auth + spoke-wired,
  brandIds array required, reason ≥ 5 chars. Audit on both paths.

UI (BrandPicker on the Billing tab, below subscription actions)
- Full plan (brandCount=0): shows "all brands automatic" badge list,
  no picker.
- Active/trial with brandCount > 0: checkbox grid of all active brands.
  Clicking past the plan limit shows an inline error. Visual deltas:
  outline "kaldırılacak" badge on rows being removed, default "yeni"
  badge on rows being added.
- Cancelled/expired: read-only message.
- Save button enabled only when selection is exactly plan.brandCount,
  there's a dirty diff, and a reason ≥ 5 chars is entered. Saves +
  router.refresh().

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 10:38:34 +03:00
2026-05-18 10:38:34 +03:00
2026-05-13 09:30:27 +00:00

Süper Panel

Internal admin panel — sp.semih.ai — Tailscale-only access.

Phase 0 skeleton: Next.js 16 + Better Auth (email/password + TOTP) + Prisma + Postgres + Redis. Deployed on Coolify, fronted by Traefik with Let's Encrypt (Cloudflare DNS-01).

See super-panel-prd.md for the full PRD.

Local dev

pnpm install
cp .env.example .env
# fill DATABASE_URL_PANEL, BETTER_AUTH_SECRET (32+ chars)
pnpm --filter @panel/web prisma:generate
pnpm dev

Production deploy

Pushes to main trigger Coolify deploy via webhook.

Deploy status

First production deploy: 2026-05-13. Live at https://sp.semih.ai (Tailscale only).

Description
Süper Panel - internal admin panel (sp.semih.ai)
Readme 4.1 MiB
Languages
TypeScript 99.2%
CSS 0.4%
Shell 0.2%
Dockerfile 0.2%