Expand AITURK Turkish settings and desktop surfaces for beta 5

This commit is contained in:
yilsem committed 2026-09-06 05:03:32 +03:00
1 parent 237d1e2060
commit 5e76f58637
108 files changed
+6675 -1034

No files matched your search

+46 -53
View File
@@ -1,4 +1,5 @@
import type { BillingRefusal } from './api'
import { type BillingCopy, DEFAULT_BILLING_COPY } from './copy'
export interface BillingRefusalPresentation {
action: { type: 'none' } | { type: 'portal'; url?: string } | { type: 'retry' } | { type: 'step_up' }
@@ -8,51 +9,51 @@ export interface BillingRefusalPresentation {
const portalAction = (url?: string): BillingRefusalPresentation['action'] => ({ type: 'portal', url })
const retryMessage = (refusal: BillingRefusal): string => {
const mins = refusal.retryAfter ? ` (try again in ~${Math.max(1, Math.round(refusal.retryAfter / 60))} min)` : ''
const retryMessage = (refusal: BillingRefusal, copy: BillingCopy): string => {
const mins = refusal.retryAfter ? copy.retryAfter(Math.max(1, Math.round(refusal.retryAfter / 60))) : ''
return `🟡 Too many charges right now${mins}. This isn't a payment failure.`
return copy.rateLimited(mins)
}
const stripeRetryMessage = (refusal: BillingRefusal): string => {
const mins = refusal.retryAfter ? ` (try again in ~${Math.max(1, Math.round(refusal.retryAfter / 60))} min)` : ''
const stripeRetryMessage = (refusal: BillingRefusal, copy: BillingCopy): string => {
const mins = refusal.retryAfter ? copy.retryAfter(Math.max(1, Math.round(refusal.retryAfter / 60))) : ''
return `Stripe is having trouble — try again shortly${mins}`
return copy.stripeRetry(mins)
}
export const resolveRefusal = (refusal: BillingRefusal): BillingRefusalPresentation => {
export const resolveRefusal = (
refusal: BillingRefusal,
copy: BillingCopy = DEFAULT_BILLING_COPY
): BillingRefusalPresentation => {
switch (refusal.kind) {
case 'consent_required':
return {
action: portalAction(refusal.portalUrl),
message: 'Confirm this card for terminal charges in the portal',
title: 'Card confirmation needed'
message: copy.confirmCardMessage,
title: copy.confirmCardTitle
}
case 'insufficient_scope':
return {
action: { type: 'step_up' },
message: 'This needs Remote Spending allowed. Start a top-up to allow it, then retry.',
title: 'Remote Spending needs approval'
message: copy.spendingApprovalMessage,
title: copy.spendingApprovalTitle
}
case 'remote_spending_revoked': {
const who =
refusal.actor === 'admin'
? 'An admin stopped remote spending for this terminal.'
: 'You stopped remote spending for this terminal.'
const who = refusal.actor === 'admin' ? copy.adminRevoked : copy.userRevoked
return {
action: portalAction(refusal.portalUrl),
message: `${who} Reconnect from Settings → Gateway to re-authorize this device.`,
title: 'Remote spending was stopped'
message: copy.reauthorize(who),
title: copy.spendingRevoked
}
}
case 'session_revoked':
return {
action: portalAction(refusal.portalUrl),
message: 'Your session was logged out. Sign in again from Settings → Gateway.',
title: 'Session logged out'
message: copy.sessionRevokedMessage,
title: copy.sessionRevokedTitle
}
case 'cli_billing_disabled':
@@ -60,50 +61,44 @@ export const resolveRefusal = (refusal: BillingRefusal): BillingRefusalPresentat
case 'remote_spending_disabled':
return {
action: portalAction(refusal.portalUrl),
message:
"Remote spending is off for this account — a billing admin can turn it on from the portal's Hermes Agent page.",
title: 'Remote spending is off'
message: copy.spendingDisabledMessage,
title: copy.spendingDisabledTitle
}
case 'role_required':
return {
action: portalAction(refusal.portalUrl),
message: 'Adding funds needs an org admin/owner. Ask an admin, or manage on the portal.',
title: 'Admin role required'
message: copy.adminRequiredMessage,
title: copy.adminRequiredTitle
}
case 'idempotency_conflict':
return {
action: { type: 'none' },
message: '🔴 That charge key was already used for a different amount. Start a fresh top-up.',
title: 'Start a fresh top-up'
message: copy.conflictingChargeMessage,
title: copy.conflictingChargeTitle
}
case 'no_payment_method':
return {
action: portalAction(refusal.portalUrl),
message:
'💳 No saved card for terminal charges yet. Set one up on the portal ' +
"(one-time credit buys don't save a reusable card).",
title: 'No saved card'
message: copy.noSavedCardMessage,
title: copy.noSavedCard
}
case 'org_access_denied':
return {
action: { type: 'none' },
message: "This token isn't bound to an org you can manage",
title: 'Org access denied'
message: copy.orgDeniedMessage,
title: copy.orgDeniedTitle
}
case 'monthly_cap_exceeded': {
const remaining = refusal.payload?.remainingUsd
return {
action: portalAction(refusal.portalUrl),
message:
remaining != null
? `🔴 Monthly spend cap reached — $${remaining} headroom left.`
: '🔴 Monthly spend cap reached.',
title: 'Monthly spend cap reached'
message: remaining != null ? copy.monthlyRemaining(String(remaining)) : copy.monthlyReached,
title: copy.monthlyReachedTitle
}
}
@@ -112,52 +107,50 @@ export const resolveRefusal = (refusal: BillingRefusal): BillingRefusalPresentat
case 'temporarily_unavailable':
return {
action: { type: 'retry' },
message: retryMessage(refusal),
title: 'Too many charges right now'
message: retryMessage(refusal, copy),
title: copy.rateLimitedTitle
}
case 'stripe_unavailable':
return {
action: { type: 'retry' },
message: stripeRetryMessage(refusal),
title: 'Stripe is having trouble'
message: stripeRetryMessage(refusal, copy),
title: copy.stripeTitle
}
case 'upgrade_cap_exceeded':
return {
action: { type: 'none' },
message: 'Daily plan-change limit reached — try again tomorrow',
title: 'Daily plan-change limit reached'
message: copy.dailyLimitMessage,
title: copy.dailyLimitTitle
}
case 'endpoint_unavailable':
return {
action: { type: 'retry' },
message:
refusal.message ||
'Billing endpoint returned a non-JSON response (it may not be available on this deployment).',
title: 'Billing endpoint unavailable'
message: refusal.message || copy.endpointMessage,
title: copy.endpointTitle
}
case 'timeout':
return {
action: { type: 'retry' },
message: refusal.message || 'Billing request timed out.',
title: 'Billing request timed out'
message: refusal.message || copy.requestTimeoutMessage,
title: copy.requestTimeoutTitle
}
case 'transport':
return {
action: { type: 'retry' },
message: refusal.message || 'Billing request failed before reaching the gateway.',
title: 'Billing connection failed'
message: refusal.message || copy.connectionFailedMessage,
title: copy.connectionFailedTitle
}
default:
return {
action: { type: 'none' },
message: refusal.message || 'Billing request failed.',
title: 'Billing request failed'
message: refusal.message || copy.requestFailedMessage,
title: copy.requestFailedTitle
}
}
}