@fayz-ai/core 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access/denial.d.ts +21 -0
- package/dist/access/denial.d.ts.map +1 -0
- package/dist/access/engine.d.ts +63 -0
- package/dist/access/engine.d.ts.map +1 -0
- package/dist/access/index.cjs +60 -0
- package/dist/access/index.cjs.map +1 -0
- package/dist/access/index.d.ts +6 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/index.js +51 -0
- package/dist/access/index.js.map +1 -0
- package/dist/access/limits.d.ts +10 -0
- package/dist/access/limits.d.ts.map +1 -0
- package/dist/address/index.d.ts +52 -0
- package/dist/address/index.d.ts.map +1 -0
- package/dist/{chunk-WKZVSKEU.js → chunk-CU64QI4A.js} +2 -2
- package/dist/chunk-CU64QI4A.js.map +1 -0
- package/dist/{chunk-54SRL7AJ.cjs → chunk-CYAWDW44.cjs} +141 -84
- package/dist/chunk-CYAWDW44.cjs.map +1 -0
- package/dist/{chunk-KNIVT6XZ.js → chunk-JHGXROKV.js} +230 -2
- package/dist/chunk-JHGXROKV.js.map +1 -0
- package/dist/{chunk-QR7U2URY.cjs → chunk-SO3VUKGZ.cjs} +230 -2
- package/dist/chunk-SO3VUKGZ.cjs.map +1 -0
- package/dist/{chunk-2NTHNWEH.cjs → chunk-UKU4RIWM.cjs} +2 -2
- package/dist/chunk-UKU4RIWM.cjs.map +1 -0
- package/dist/{chunk-ZBIWYDAR.js → chunk-XJKW2JRW.js} +140 -85
- package/dist/chunk-XJKW2JRW.js.map +1 -0
- package/dist/{chunk-CHGX6UYR.js → chunk-YH25Y4FW.js} +6 -3
- package/dist/chunk-YH25Y4FW.js.map +1 -0
- package/dist/{chunk-5X2VX3RQ.cjs → chunk-ZOPJB5FN.cjs} +6 -3
- package/dist/chunk-ZOPJB5FN.cjs.map +1 -0
- package/dist/data/count.d.ts +34 -0
- package/dist/data/count.d.ts.map +1 -0
- package/dist/data/index.cjs +18 -10
- package/dist/data/index.d.ts +2 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +1 -1
- package/dist/data/resolve.d.ts.map +1 -1
- package/dist/entity/index.cjs +6 -6
- package/dist/entity/index.js +1 -1
- package/dist/entity/registry.d.ts +9 -0
- package/dist/entity/registry.d.ts.map +1 -1
- package/dist/i18n/index.cjs +10 -10
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/shell-translations.d.ts.map +1 -1
- package/dist/index.cjs +852 -124
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +765 -56
- package/dist/index.js.map +1 -1
- package/dist/manifest/index.d.ts +109 -3
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/serialize.d.ts +14 -0
- package/dist/manifest/serialize.d.ts.map +1 -0
- package/dist/plugin/index.cjs +7 -7
- package/dist/plugin/index.js +1 -1
- package/dist/plugin/runtime.d.ts.map +1 -1
- package/dist/types/billing.d.ts +20 -0
- package/dist/types/billing.d.ts.map +1 -1
- package/dist/types/crud.d.ts +48 -2
- package/dist/types/crud.d.ts.map +1 -1
- package/dist/types/entitlements.d.ts +45 -0
- package/dist/types/entitlements.d.ts.map +1 -0
- package/dist/types/index.d.ts +4 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/plugins.d.ts +84 -0
- package/dist/types/plugins.d.ts.map +1 -1
- package/package.json +14 -4
- package/src/access/denial.ts +33 -0
- package/src/access/engine.test.ts +48 -0
- package/src/access/engine.ts +121 -0
- package/src/access/index.ts +13 -0
- package/src/access/limits.ts +18 -0
- package/src/address/index.ts +188 -0
- package/src/data/count.test.ts +152 -0
- package/src/data/count.ts +131 -0
- package/src/data/index.ts +2 -0
- package/src/data/resolve.ts +7 -1
- package/src/entity/registry.ts +7 -0
- package/src/i18n/shell-translations.ts +230 -0
- package/src/index.ts +18 -3
- package/src/manifest/app-manifest.schema.json +618 -45
- package/src/manifest/index.ts +127 -3
- package/src/manifest/serialize.ts +52 -0
- package/src/plugin/runtime.ts +5 -1
- package/src/types/billing.ts +22 -0
- package/src/types/crud.ts +49 -2
- package/src/types/entitlements.ts +45 -0
- package/src/types/index.ts +4 -3
- package/src/types/plugins.ts +79 -0
- package/dist/chunk-2NTHNWEH.cjs.map +0 -1
- package/dist/chunk-54SRL7AJ.cjs.map +0 -1
- package/dist/chunk-5X2VX3RQ.cjs.map +0 -1
- package/dist/chunk-CHGX6UYR.js.map +0 -1
- package/dist/chunk-KNIVT6XZ.js.map +0 -1
- package/dist/chunk-QR7U2URY.cjs.map +0 -1
- package/dist/chunk-WKZVSKEU.js.map +0 -1
- package/dist/chunk-ZBIWYDAR.js.map +0 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { getSupabaseClientOptional } from './supabase'
|
|
2
|
+
import { getActiveTenantId } from '../tenant'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tenant-scoped row counter — the single cheap `count(*)` helper the access
|
|
6
|
+
* engine uses to resolve quantity limits. Uses Supabase's `head:true` count so
|
|
7
|
+
* no rows are transferred. Results are cached briefly per tenant+table+filter to
|
|
8
|
+
* survive the burst of reads a page does (nav badge + gate + list), and can be
|
|
9
|
+
* force-invalidated by creates via {@link invalidateCount}.
|
|
10
|
+
*
|
|
11
|
+
* MOCK MODE NEVER BLOCKS: with no global Supabase client the count is `0`, so
|
|
12
|
+
* limits always read as "well under cap" — offline/demo apps stay fully usable.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface CountByTenantOptions {
|
|
16
|
+
/** Optional `kind` column filter for multi-kind tables. */
|
|
17
|
+
kind?: string
|
|
18
|
+
/** `'month'` counts rows created since the 1st of the current month; `'total'` (default) counts all. */
|
|
19
|
+
period?: 'month' | 'total'
|
|
20
|
+
/** Tenant override; defaults to the active tenant (getActiveTenantId). */
|
|
21
|
+
tenantId?: string
|
|
22
|
+
/** Skip the short-lived cache and hit the DB (used by fresh guard checks). */
|
|
23
|
+
fresh?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const TTL_MS = 15_000
|
|
27
|
+
|
|
28
|
+
interface CacheEntry {
|
|
29
|
+
value: number
|
|
30
|
+
expires: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const cache = new Map<string, CacheEntry>()
|
|
34
|
+
|
|
35
|
+
function cacheKey(table: string, tenantId: string, kind: string | undefined, period: string): string {
|
|
36
|
+
return `${tenantId}::${table}::${kind ?? ''}::${period}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function startOfMonthISO(): string {
|
|
40
|
+
const now = new Date()
|
|
41
|
+
return new Date(now.getFullYear(), now.getMonth(), 1).toISOString()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* How long a usage count may take before we give up on it. Failing OPEN on an
|
|
46
|
+
* error was always the policy (see below); a request that never settles is the
|
|
47
|
+
* same failure with worse symptoms — a caller awaiting this (the create guards)
|
|
48
|
+
* hangs forever, which reads to the user as a dead button. Observed on a cold
|
|
49
|
+
* boot, where the query can be issued before the auth session is ready.
|
|
50
|
+
*/
|
|
51
|
+
const COUNT_TIMEOUT_MS = 5_000
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Count rows in `table` for the given (or active) tenant.
|
|
55
|
+
* Returns 0 when no Supabase client is registered (mock mode).
|
|
56
|
+
*/
|
|
57
|
+
export async function countByTenant(table: string, options: CountByTenantOptions = {}): Promise<number> {
|
|
58
|
+
const tenantId = options.tenantId ?? getActiveTenantId()
|
|
59
|
+
if (!tenantId) return 0
|
|
60
|
+
|
|
61
|
+
const period = options.period ?? 'total'
|
|
62
|
+
const key = cacheKey(table, tenantId, options.kind, period)
|
|
63
|
+
|
|
64
|
+
if (!options.fresh) {
|
|
65
|
+
const hit = cache.get(key)
|
|
66
|
+
if (hit && hit.expires > Date.now()) return hit.value
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const client = getSupabaseClientOptional() as {
|
|
70
|
+
from: (t: string) => {
|
|
71
|
+
select: (columns: string, opts: { count: 'exact'; head: true }) => {
|
|
72
|
+
eq: (col: string, val: unknown) => unknown
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} | null
|
|
76
|
+
|
|
77
|
+
// No client → mock/offline mode: never block.
|
|
78
|
+
if (!client) return 0
|
|
79
|
+
|
|
80
|
+
let query = client.from(table).select('*', { count: 'exact', head: true }).eq('tenant_id', tenantId) as {
|
|
81
|
+
eq: (col: string, val: unknown) => unknown
|
|
82
|
+
gte: (col: string, val: unknown) => unknown
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (options.kind) {
|
|
86
|
+
query = query.eq('kind', options.kind) as typeof query
|
|
87
|
+
}
|
|
88
|
+
if (period === 'month') {
|
|
89
|
+
query = query.gte('created_at', startOfMonthISO()) as typeof query
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// On error OR timeout, fail OPEN (return 0) — a counting failure must never
|
|
93
|
+
// wall a tenant out of their own product, and must never hang the UI awaiting
|
|
94
|
+
// it. Enforcement's source of truth is the DB anyway.
|
|
95
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
96
|
+
const timeout = new Promise<{ count: null; error: unknown; timedOut: true }>((resolve) => {
|
|
97
|
+
timer = setTimeout(() => resolve({ count: null, error: 'timeout', timedOut: true }), COUNT_TIMEOUT_MS)
|
|
98
|
+
})
|
|
99
|
+
const settled = await Promise.race([
|
|
100
|
+
query as unknown as Promise<{ count: number | null; error: unknown }>,
|
|
101
|
+
timeout,
|
|
102
|
+
])
|
|
103
|
+
if (timer) clearTimeout(timer)
|
|
104
|
+
|
|
105
|
+
const { count, error } = settled
|
|
106
|
+
const value = error ? 0 : count ?? 0
|
|
107
|
+
|
|
108
|
+
// A timed-out count is not a fact about the tenant — caching it would make one
|
|
109
|
+
// slow request suppress the guard for the whole TTL.
|
|
110
|
+
if (!(settled as { timedOut?: true }).timedOut) {
|
|
111
|
+
cache.set(key, { value, expires: Date.now() + TTL_MS })
|
|
112
|
+
}
|
|
113
|
+
return value
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Drop cached counts. Called by create handlers (via invalidateLimit) so the
|
|
118
|
+
* next read reflects the row they just added.
|
|
119
|
+
*
|
|
120
|
+
* @param tenantOrKey Substring matched against cache keys (a tenant id, a table
|
|
121
|
+
* name, or any fragment). Omit to clear the entire cache.
|
|
122
|
+
*/
|
|
123
|
+
export function invalidateCount(tenantOrKey?: string): void {
|
|
124
|
+
if (!tenantOrKey) {
|
|
125
|
+
cache.clear()
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
for (const key of cache.keys()) {
|
|
129
|
+
if (key.includes(tenantOrKey)) cache.delete(key)
|
|
130
|
+
}
|
|
131
|
+
}
|
package/src/data/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { createSupabaseProvider, setGlobalSupabaseClient, getSupabaseClientOptional } from './supabase'
|
|
2
|
+
export { countByTenant, invalidateCount } from './count'
|
|
3
|
+
export type { CountByTenantOptions } from './count'
|
|
2
4
|
export { getFayzCloudClient, FAYZ_CLOUD_URL, FAYZ_CLOUD_PUBLISHABLE_KEY } from './cloud'
|
|
3
5
|
export { createFayzApiProvider } from './platform-api'
|
|
4
6
|
export { createMockProvider } from './mock'
|
package/src/data/resolve.ts
CHANGED
|
@@ -66,8 +66,14 @@ export function resolveDataProvider<T extends { id: string }>(
|
|
|
66
66
|
|
|
67
67
|
if (client && entityDef.data?.table) {
|
|
68
68
|
const tenantId = () => getActiveTenantId()
|
|
69
|
+
// Archetype entities share a physical table (e.g. people) across kinds
|
|
70
|
+
// (student/teacher/staff…). The cache key must carry the kind or the first
|
|
71
|
+
// kind's resultset is served to every sibling entity (teachers page
|
|
72
|
+
// listing students).
|
|
69
73
|
const cacheOptions = {
|
|
70
|
-
table: entityDef.data.
|
|
74
|
+
table: entityDef.data.archetypeKind
|
|
75
|
+
? `${entityDef.data.table}#${entityDef.data.archetypeKind}`
|
|
76
|
+
: entityDef.data.table,
|
|
71
77
|
tenantId,
|
|
72
78
|
ttl: entityDef.data.cacheTTL,
|
|
73
79
|
}
|
package/src/entity/registry.ts
CHANGED
|
@@ -8,6 +8,13 @@ export interface RegisteredEntity {
|
|
|
8
8
|
icon?: string
|
|
9
9
|
fields: EntityDef['fields']
|
|
10
10
|
source: 'app' | 'plugin'
|
|
11
|
+
/**
|
|
12
|
+
* The definition itself, so consumers that need to *read* the entity — the AI
|
|
13
|
+
* tool executor resolving a data provider — are not limited to the display
|
|
14
|
+
* metadata. Optional: registrations made before this existed omit it.
|
|
15
|
+
*/
|
|
16
|
+
entityDef?: EntityDef
|
|
17
|
+
mockData?: Array<{ id: string }>
|
|
11
18
|
archetype?: EntityArchetype
|
|
12
19
|
pluginId?: string
|
|
13
20
|
pluginName?: string
|
|
@@ -137,6 +137,7 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
137
137
|
"crud.import.rowNumber": "Row {{row}}:",
|
|
138
138
|
"crud.import.toastSuccess": "{{count}} records imported",
|
|
139
139
|
"crud.import.noConnection": "Not connected to the database. Import requires an active connection.",
|
|
140
|
+
"crud.import.limitExceeded": "This import exceeds your plan limit. Upgrade or import fewer rows.",
|
|
140
141
|
"crud.export.action": "Export",
|
|
141
142
|
"crud.export.noData": "No data to export.",
|
|
142
143
|
"crud.export.toastSuccess": "{{count}} records exported",
|
|
@@ -199,6 +200,17 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
199
200
|
"billing.title": "Billing",
|
|
200
201
|
"billing.subtitle": "Manage your subscription and billing.",
|
|
201
202
|
"billing.currentPlan": "Current Plan",
|
|
203
|
+
"billing.subscription.title": "Subscription",
|
|
204
|
+
"billing.subscription.subtitle": "Manage your plan and unlock more features.",
|
|
205
|
+
"billing.changeToPlan": "Switch to this plan",
|
|
206
|
+
"billing.featureMatrix": "Feature comparison",
|
|
207
|
+
"billing.changePlanConfirmTitle": "Change plan?",
|
|
208
|
+
"billing.changePlanConfirmBody": "You're switching to the {{plan}} plan.",
|
|
209
|
+
"billing.changePlanConfirm": "Switch plan",
|
|
210
|
+
"billing.planChanged": "You're now on the {{plan}} plan.",
|
|
211
|
+
"billing.planChangeFailed": "Couldn't change your plan. Please try again.",
|
|
212
|
+
"billing.noPlans": "No plans are configured.",
|
|
213
|
+
"billing.pro": "Pro",
|
|
202
214
|
"billing.upgrade": "Upgrade",
|
|
203
215
|
"billing.downgrade": "Downgrade",
|
|
204
216
|
"billing.monthly": "Monthly",
|
|
@@ -259,11 +271,68 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
259
271
|
"organization.onboarding.failedCreate": "Failed to create workspace",
|
|
260
272
|
"settings.title": "Settings",
|
|
261
273
|
"settings.subtitle": "Manage your account and organization preferences.",
|
|
274
|
+
"settings.backToHome": "Back to home",
|
|
275
|
+
"settings.navLinks": "Settings links",
|
|
262
276
|
"settings.general": "General",
|
|
263
277
|
"settings.properties": "Properties",
|
|
264
278
|
"settings.profile": "Profile",
|
|
265
279
|
"settings.branding": "Branding",
|
|
266
280
|
"settings.security": "Security",
|
|
281
|
+
"settings.subscription": "Subscription",
|
|
282
|
+
"upgrade.title": 'Premium feature',
|
|
283
|
+
"upgrade.description": 'This feature is available on a higher plan. Upgrade to unlock it.',
|
|
284
|
+
"upgrade.descriptionFeature": 'The {feature} feature is available on a higher plan. Upgrade to unlock it.',
|
|
285
|
+
// ContactPicker — shared "find the person, or create them" flow.
|
|
286
|
+
"contactPicker.search": 'Search contact...',
|
|
287
|
+
"contactPicker.newContact": 'New contact',
|
|
288
|
+
"contactPicker.name": 'Name',
|
|
289
|
+
"contactPicker.phone": 'Phone',
|
|
290
|
+
"contactPicker.emailOptional": 'Email (optional)',
|
|
291
|
+
"contactPicker.create": 'Create',
|
|
292
|
+
"contactPicker.creating": 'Creating...',
|
|
293
|
+
"contactPicker.cancel": 'Cancel',
|
|
294
|
+
"contactPicker.clear": 'Clear contact',
|
|
295
|
+
"contactPicker.createFailed": 'Could not create the contact',
|
|
296
|
+
"contactPicker.addField": 'Add {label}',
|
|
297
|
+
|
|
298
|
+
"upgrade.includes": 'Includes {feature}',
|
|
299
|
+
"upgrade.cta": 'View plans',
|
|
300
|
+
"upgrade.badgeTooltip": 'Available on a higher plan',
|
|
301
|
+
"upgrade.unlock": 'Unlock {feature}',
|
|
302
|
+
"upgrade.currentPlan": 'Your plan: {plan}',
|
|
303
|
+
"upgrade.recommended": 'Recommended',
|
|
304
|
+
"upgrade.ctaUpgrade": 'Upgrade',
|
|
305
|
+
"upgrade.ctaUpgradeTo": 'Upgrade to {plan}',
|
|
306
|
+
"upgrade.compareAll": 'Compare all plans',
|
|
307
|
+
"upgrade.valueGeneric": 'Get everything you need to grow — included in the plans below.',
|
|
308
|
+
"upgrade.value.marketing": 'Campaigns, funnel and content to grow your client base.',
|
|
309
|
+
"upgrade.value.reports": 'Advanced reports and insights to make sharper decisions.',
|
|
310
|
+
"upgrade.value.fin_reconciliation": 'Reconcile payments and keep your cash flow under control.',
|
|
311
|
+
"billing.unlimited": 'Unlimited',
|
|
312
|
+
"billing.upTo": 'Up to {count}',
|
|
313
|
+
"limit.label.clients": 'clients',
|
|
314
|
+
"limit.label.users": 'team members',
|
|
315
|
+
"limit.label.locations": 'locations',
|
|
316
|
+
"limit.label.bookings_month": 'monthly bookings',
|
|
317
|
+
"limit.label.products": 'products',
|
|
318
|
+
"limit.label.students": 'students',
|
|
319
|
+
"limit.label.courses": 'courses',
|
|
320
|
+
"limit.label.contacts": 'contacts',
|
|
321
|
+
"limit.label.deals": 'deals',
|
|
322
|
+
"limit.label.menu_items": 'menu items',
|
|
323
|
+
"limit.label.orders_month": 'monthly orders',
|
|
324
|
+
"limit.label.connected_banks": 'connected banks',
|
|
325
|
+
"limit.label.movements_month": 'monthly movements',
|
|
326
|
+
"limit.label.leads": 'leads',
|
|
327
|
+
"limit.label.quotes": 'quotes',
|
|
328
|
+
"limit.label.conversations_month": 'monthly conversations',
|
|
329
|
+
"limit.label.recipes": 'recipes',
|
|
330
|
+
"limit.label.form_templates": 'form templates',
|
|
331
|
+
"limit.label.documents_month": 'monthly documents',
|
|
332
|
+
"limit.label.content_posts_month": 'monthly content posts',
|
|
333
|
+
"limit.label.blog_posts": 'blog posts',
|
|
334
|
+
"limit.reached": 'Você atingiu o limite de {label} do plano {plan}.',
|
|
335
|
+
"banner.overLimit": 'Você excedeu o limite de {label} do seu plano — faça upgrade para continuar crescendo.',
|
|
267
336
|
"settings.team": "Team",
|
|
268
337
|
"settings.permissions": "Permissions",
|
|
269
338
|
"settings.locations": "Locations",
|
|
@@ -303,6 +372,10 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
303
372
|
"app.notFound.goToDashboard": "Go to Dashboard",
|
|
304
373
|
"app.accessDenied.heading": "Access Denied",
|
|
305
374
|
"app.accessDenied.message": "You don't have permission to view this page.",
|
|
375
|
+
"shell.accessDenied.title": "Access restricted",
|
|
376
|
+
"shell.accessDenied.message": "You don't have permission to view this page.",
|
|
377
|
+
"organization.workspaces": "Workspaces",
|
|
378
|
+
"organization.onlyWorkspace": "This is your only workspace.",
|
|
306
379
|
"auth.login.enterCredentials": "Enter your credentials to access your account",
|
|
307
380
|
"auth.login.failedDefault": "Failed to sign in. Please try again.",
|
|
308
381
|
"auth.login.failedWithProvider": "Failed to sign in with {{provider}}.",
|
|
@@ -343,6 +416,17 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
343
416
|
"chat.tryAsking": "Try asking",
|
|
344
417
|
"chat.fab.tryIt": "Try it",
|
|
345
418
|
"chat.messagePlaceholder": "Message...",
|
|
419
|
+
"chat.notConfigured": "Assistant not configured — set chat.apiEndpoint to enable conversations.",
|
|
420
|
+
"chat.confirmAction.title": "Confirm this action?",
|
|
421
|
+
"chat.confirmAction.confirm": "Confirm",
|
|
422
|
+
"chat.confirmAction.cancel": "Cancel",
|
|
423
|
+
"chat.confirmAction.blocked": "Respond to the confirmation above first",
|
|
424
|
+
"chat.signingIn": "One moment — still confirming your session.",
|
|
425
|
+
"chat.history": "Previous conversations",
|
|
426
|
+
"chat.newConversation": "New conversation",
|
|
427
|
+
"chat.untitledConversation": "Untitled conversation",
|
|
428
|
+
"chat.renameConversation": "Rename",
|
|
429
|
+
"chat.saveTitle": "Save title",
|
|
346
430
|
"chat.toolsAvailable": "{{count}} tools available",
|
|
347
431
|
"chat.suggestion.dashboard.kpi-summary.0": "How is my business doing today?",
|
|
348
432
|
"chat.suggestion.dashboard.kpi-summary.1": "What's my revenue today?",
|
|
@@ -586,6 +670,38 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
586
670
|
"crud.archetype.activity.description": "View a chronological log of all actions, changes, and events related to this person.",
|
|
587
671
|
"crud.archetype.documents.title": "Documents",
|
|
588
672
|
"crud.archetype.documents.description": "Attach and manage files, contracts, certifications, and other documents for this person.",
|
|
673
|
+
"crud.archetype.addresses.add": "Add address",
|
|
674
|
+
"crud.archetype.addresses.addTitle": "New address",
|
|
675
|
+
"crud.archetype.addresses.editTitle": "Edit address",
|
|
676
|
+
"crud.archetype.addresses.formDescription": "Postal code, street and city are required; everything else is optional.",
|
|
677
|
+
"crud.archetype.addresses.emptyTitle": "No address on file",
|
|
678
|
+
"crud.archetype.addresses.emptyDescription": "Add where this person is delivered to, collected from, or billed at.",
|
|
679
|
+
"crud.archetype.addresses.fallbackLabel": "Address",
|
|
680
|
+
"crud.archetype.addresses.labelPlaceholder": "Home, Work…",
|
|
681
|
+
"crud.archetype.addresses.default": "Default",
|
|
682
|
+
"crud.archetype.addresses.setDefault": "Use as the default address",
|
|
683
|
+
"crud.archetype.addresses.kind.both": "Shipping and billing",
|
|
684
|
+
"crud.archetype.addresses.kind.shipping": "Shipping",
|
|
685
|
+
"crud.archetype.addresses.kind.billing": "Billing",
|
|
686
|
+
"crud.archetype.addresses.field.label": "Label",
|
|
687
|
+
"crud.archetype.addresses.field.kind": "Use",
|
|
688
|
+
"crud.archetype.addresses.field.postalCode": "Postal code",
|
|
689
|
+
"crud.archetype.addresses.field.street": "Street",
|
|
690
|
+
"crud.archetype.addresses.field.number": "Number",
|
|
691
|
+
"crud.archetype.addresses.field.complement": "Complement",
|
|
692
|
+
"crud.archetype.addresses.field.district": "District",
|
|
693
|
+
"crud.archetype.addresses.field.city": "City",
|
|
694
|
+
"crud.archetype.addresses.field.state": "State",
|
|
695
|
+
"crud.archetype.addresses.field.recipient": "Recipient",
|
|
696
|
+
"crud.archetype.addresses.field.phone": "Phone",
|
|
697
|
+
"crud.archetype.addresses.created": "Address added",
|
|
698
|
+
"crud.archetype.addresses.updated": "Address updated",
|
|
699
|
+
"crud.archetype.addresses.deleted": "Address removed",
|
|
700
|
+
"crud.archetype.addresses.deleteTitle": "Remove this address?",
|
|
701
|
+
"crud.archetype.addresses.loadFailed": "Could not load the addresses.",
|
|
702
|
+
"crud.archetype.addresses.saveFailed": "Could not save the address.",
|
|
703
|
+
"crud.archetype.addresses.deleteFailed": "Could not remove the address.",
|
|
704
|
+
"crud.archetype.addresses.noTenant": "No active organization — reload the page and try again.",
|
|
589
705
|
"crud.archetype.schedule.title": "Schedule",
|
|
590
706
|
"crud.archetype.schedule.description": "View and manage appointments, shifts, and availability for this person.",
|
|
591
707
|
"crud.archetype.inventory.title": "Inventory & Stock",
|
|
@@ -858,6 +974,17 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
858
974
|
"billing.title": "Faturamento",
|
|
859
975
|
"billing.subtitle": "Gerencie sua assinatura e faturamento.",
|
|
860
976
|
"billing.currentPlan": "Plano Atual",
|
|
977
|
+
"billing.subscription.title": "Assinatura",
|
|
978
|
+
"billing.subscription.subtitle": "Gerencie seu plano e desbloqueie mais recursos.",
|
|
979
|
+
"billing.changeToPlan": "Mudar para este plano",
|
|
980
|
+
"billing.featureMatrix": "Comparativo de recursos",
|
|
981
|
+
"billing.changePlanConfirmTitle": "Mudar de plano?",
|
|
982
|
+
"billing.changePlanConfirmBody": "Você está mudando para o plano {{plan}}.",
|
|
983
|
+
"billing.changePlanConfirm": "Mudar de plano",
|
|
984
|
+
"billing.planChanged": "Agora você está no plano {{plan}}.",
|
|
985
|
+
"billing.planChangeFailed": "Não foi possível mudar seu plano. Tente novamente.",
|
|
986
|
+
"billing.noPlans": "Nenhum plano configurado.",
|
|
987
|
+
"billing.pro": "Pro",
|
|
861
988
|
"billing.upgrade": "Fazer Upgrade",
|
|
862
989
|
"billing.downgrade": "Fazer Downgrade",
|
|
863
990
|
"billing.monthly": "Mensal",
|
|
@@ -915,11 +1042,66 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
915
1042
|
"organization.onboarding.createWorkspace": "Criar Espaço de Trabalho",
|
|
916
1043
|
"settings.title": "Configurações",
|
|
917
1044
|
"settings.subtitle": "Gerencie sua conta e preferências da organização.",
|
|
1045
|
+
"settings.backToHome": "Voltar para o início",
|
|
1046
|
+
"settings.navLinks": "Links de configuração",
|
|
918
1047
|
"settings.general": "Geral",
|
|
919
1048
|
"settings.properties": "Propriedades",
|
|
920
1049
|
"settings.profile": "Perfil",
|
|
921
1050
|
"settings.branding": "Marca",
|
|
922
1051
|
"settings.security": "Segurança",
|
|
1052
|
+
"settings.subscription": "Assinatura",
|
|
1053
|
+
"upgrade.title": 'Recurso premium',
|
|
1054
|
+
"upgrade.description": 'Este recurso está disponível em um plano superior. Faça upgrade para liberá-lo.',
|
|
1055
|
+
"upgrade.descriptionFeature": 'O recurso {feature} está disponível em um plano superior. Faça upgrade para liberá-lo.',
|
|
1056
|
+
// ContactPicker — busca-ou-cadastra compartilhado.
|
|
1057
|
+
"contactPicker.search": 'Buscar contato...',
|
|
1058
|
+
"contactPicker.newContact": 'Novo contato',
|
|
1059
|
+
"contactPicker.name": 'Nome',
|
|
1060
|
+
"contactPicker.phone": 'Telefone',
|
|
1061
|
+
"contactPicker.emailOptional": 'E-mail (opcional)',
|
|
1062
|
+
"contactPicker.create": 'Cadastrar',
|
|
1063
|
+
"contactPicker.creating": 'Cadastrando...',
|
|
1064
|
+
"contactPicker.cancel": 'Cancelar',
|
|
1065
|
+
"contactPicker.clear": 'Remover contato',
|
|
1066
|
+
"contactPicker.createFailed": 'Não foi possível cadastrar o contato',
|
|
1067
|
+
"contactPicker.addField": 'Adicionar {label}',
|
|
1068
|
+
|
|
1069
|
+
"upgrade.includes": 'Inclui {feature}',
|
|
1070
|
+
"upgrade.cta": 'Ver planos',
|
|
1071
|
+
"upgrade.badgeTooltip": 'Disponível em um plano superior',
|
|
1072
|
+
"upgrade.unlock": 'Desbloqueie {feature}',
|
|
1073
|
+
"upgrade.currentPlan": 'Seu plano: {plan}',
|
|
1074
|
+
"upgrade.recommended": 'Recomendado',
|
|
1075
|
+
"upgrade.ctaUpgrade": 'Fazer upgrade',
|
|
1076
|
+
"upgrade.ctaUpgradeTo": 'Fazer upgrade para {plan}',
|
|
1077
|
+
"upgrade.compareAll": 'Comparar todos os planos',
|
|
1078
|
+
"upgrade.valueGeneric": 'Tudo o que você precisa para crescer — incluso nos planos abaixo.',
|
|
1079
|
+
"upgrade.value.marketing": 'Campanhas, funil e conteúdo para crescer sua base de clientes.',
|
|
1080
|
+
"upgrade.value.reports": 'Relatórios avançados e insights para decidir com mais precisão.',
|
|
1081
|
+
"upgrade.value.fin_reconciliation": 'Concilie pagamentos e mantenha seu fluxo de caixa sob controle.',
|
|
1082
|
+
"billing.unlimited": 'Ilimitado',
|
|
1083
|
+
"billing.upTo": 'Até {count}',
|
|
1084
|
+
"limit.label.clients": 'clientes',
|
|
1085
|
+
"limit.label.users": 'membros da equipe',
|
|
1086
|
+
"limit.label.locations": 'locais',
|
|
1087
|
+
"limit.label.bookings_month": 'agendamentos mensais',
|
|
1088
|
+
"limit.label.leads": 'leads',
|
|
1089
|
+
"limit.label.quotes": 'orçamentos',
|
|
1090
|
+
"limit.label.conversations_month": 'conversas mensais',
|
|
1091
|
+
"limit.label.recipes": 'receitas',
|
|
1092
|
+
"limit.label.form_templates": 'modelos de formulário',
|
|
1093
|
+
"limit.label.documents_month": 'documentos mensais',
|
|
1094
|
+
"limit.label.content_posts_month": 'posts de conteúdo mensais',
|
|
1095
|
+
"limit.label.blog_posts": 'posts de blog',
|
|
1096
|
+
"limit.label.products": 'produtos',
|
|
1097
|
+
"limit.label.students": 'alunos',
|
|
1098
|
+
"limit.label.courses": 'cursos',
|
|
1099
|
+
"limit.label.contacts": 'contatos',
|
|
1100
|
+
"limit.label.deals": 'negócios',
|
|
1101
|
+
"limit.label.menu_items": 'itens do cardápio',
|
|
1102
|
+
"limit.label.orders_month": 'pedidos mensais',
|
|
1103
|
+
"limit.label.connected_banks": 'bancos conectados',
|
|
1104
|
+
"limit.label.movements_month": 'movimentações mensais',
|
|
923
1105
|
"settings.team": "Equipe",
|
|
924
1106
|
"settings.permissions": "Permissões",
|
|
925
1107
|
"settings.locations": "Locais",
|
|
@@ -952,6 +1134,10 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
952
1134
|
"app.notFound.goToDashboard": "Ir para o Painel",
|
|
953
1135
|
"app.accessDenied.heading": "Acesso Negado",
|
|
954
1136
|
"app.accessDenied.message": "Você não tem permissão para acessar esta página.",
|
|
1137
|
+
"shell.accessDenied.title": "Acesso restrito",
|
|
1138
|
+
"shell.accessDenied.message": "Você não tem permissão para acessar esta página.",
|
|
1139
|
+
"organization.workspaces": "Áreas de trabalho",
|
|
1140
|
+
"organization.onlyWorkspace": "Esta é a sua única área de trabalho.",
|
|
955
1141
|
"crud.form.requiredIndicator": "*",
|
|
956
1142
|
"crud.form.createDescription": "Criar um(a) novo(a) {{entity}}.",
|
|
957
1143
|
"crud.form.editDescription": "Atualizar detalhes de {{entity}}.",
|
|
@@ -993,6 +1179,7 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
993
1179
|
"crud.import.rowNumber": "Linha {{row}}:",
|
|
994
1180
|
"crud.import.toastSuccess": "{{count}} registros importados",
|
|
995
1181
|
"crud.import.noConnection": "Sem conexão com o banco de dados. A importação requer uma conexão ativa.",
|
|
1182
|
+
"crud.import.limitExceeded": "Esta importação excede o limite do seu plano. Faça upgrade ou importe menos linhas.",
|
|
996
1183
|
"crud.export.action": "Exportar",
|
|
997
1184
|
"crud.export.noData": "Sem dados para exportar.",
|
|
998
1185
|
"crud.export.toastSuccess": "{{count}} registros exportados",
|
|
@@ -1087,6 +1274,17 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
1087
1274
|
"chat.tryAsking": "Experimente perguntar",
|
|
1088
1275
|
"chat.fab.tryIt": "Experimente",
|
|
1089
1276
|
"chat.messagePlaceholder": "Mensagem...",
|
|
1277
|
+
"chat.notConfigured": "Assistente não configurado — defina chat.apiEndpoint para habilitar conversas.",
|
|
1278
|
+
"chat.confirmAction.title": "Confirmar esta ação?",
|
|
1279
|
+
"chat.confirmAction.confirm": "Confirmar",
|
|
1280
|
+
"chat.confirmAction.cancel": "Cancelar",
|
|
1281
|
+
"chat.confirmAction.blocked": "Responda a confirmação acima primeiro",
|
|
1282
|
+
"chat.signingIn": "Um instante — ainda estou confirmando sua sessão.",
|
|
1283
|
+
"chat.history": "Conversas anteriores",
|
|
1284
|
+
"chat.newConversation": "Nova conversa",
|
|
1285
|
+
"chat.untitledConversation": "Conversa sem título",
|
|
1286
|
+
"chat.renameConversation": "Renomear",
|
|
1287
|
+
"chat.saveTitle": "Salvar título",
|
|
1090
1288
|
"chat.toolsAvailable": "{{count}} ferramentas disponíveis",
|
|
1091
1289
|
"chat.suggestion.dashboard.kpi-summary.0": "Como está meu negócio hoje?",
|
|
1092
1290
|
"chat.suggestion.dashboard.kpi-summary.1": "Qual o faturamento de hoje?",
|
|
@@ -1286,6 +1484,38 @@ export const shellTranslations: Record<string, Record<string, string>> = {
|
|
|
1286
1484
|
"crud.archetype.activity.description": "Veja um registro cronológico de todas as ações, alterações e eventos relacionados a esta pessoa.",
|
|
1287
1485
|
"crud.archetype.documents.title": "Documentos",
|
|
1288
1486
|
"crud.archetype.documents.description": "Anexe e gerencie arquivos, contratos, certificações e outros documentos desta pessoa.",
|
|
1487
|
+
"crud.archetype.addresses.add": "Adicionar endereço",
|
|
1488
|
+
"crud.archetype.addresses.addTitle": "Novo endereço",
|
|
1489
|
+
"crud.archetype.addresses.editTitle": "Editar endereço",
|
|
1490
|
+
"crud.archetype.addresses.formDescription": "CEP, rua e cidade são obrigatórios; o resto é opcional.",
|
|
1491
|
+
"crud.archetype.addresses.emptyTitle": "Nenhum endereço cadastrado",
|
|
1492
|
+
"crud.archetype.addresses.emptyDescription": "Cadastre onde esta pessoa recebe entregas, é coletada ou é cobrada.",
|
|
1493
|
+
"crud.archetype.addresses.fallbackLabel": "Endereço",
|
|
1494
|
+
"crud.archetype.addresses.labelPlaceholder": "Casa, Trabalho…",
|
|
1495
|
+
"crud.archetype.addresses.default": "Padrão",
|
|
1496
|
+
"crud.archetype.addresses.setDefault": "Usar como endereço padrão",
|
|
1497
|
+
"crud.archetype.addresses.kind.both": "Entrega e cobrança",
|
|
1498
|
+
"crud.archetype.addresses.kind.shipping": "Entrega",
|
|
1499
|
+
"crud.archetype.addresses.kind.billing": "Cobrança",
|
|
1500
|
+
"crud.archetype.addresses.field.label": "Identificação",
|
|
1501
|
+
"crud.archetype.addresses.field.kind": "Uso",
|
|
1502
|
+
"crud.archetype.addresses.field.postalCode": "CEP",
|
|
1503
|
+
"crud.archetype.addresses.field.street": "Rua",
|
|
1504
|
+
"crud.archetype.addresses.field.number": "Número",
|
|
1505
|
+
"crud.archetype.addresses.field.complement": "Complemento",
|
|
1506
|
+
"crud.archetype.addresses.field.district": "Bairro",
|
|
1507
|
+
"crud.archetype.addresses.field.city": "Cidade",
|
|
1508
|
+
"crud.archetype.addresses.field.state": "UF",
|
|
1509
|
+
"crud.archetype.addresses.field.recipient": "Quem recebe",
|
|
1510
|
+
"crud.archetype.addresses.field.phone": "Telefone",
|
|
1511
|
+
"crud.archetype.addresses.created": "Endereço adicionado",
|
|
1512
|
+
"crud.archetype.addresses.updated": "Endereço atualizado",
|
|
1513
|
+
"crud.archetype.addresses.deleted": "Endereço removido",
|
|
1514
|
+
"crud.archetype.addresses.deleteTitle": "Remover este endereço?",
|
|
1515
|
+
"crud.archetype.addresses.loadFailed": "Não foi possível carregar os endereços.",
|
|
1516
|
+
"crud.archetype.addresses.saveFailed": "Não foi possível salvar o endereço.",
|
|
1517
|
+
"crud.archetype.addresses.deleteFailed": "Não foi possível remover o endereço.",
|
|
1518
|
+
"crud.archetype.addresses.noTenant": "Nenhuma organização ativa — recarregue a página e tente de novo.",
|
|
1289
1519
|
"crud.archetype.schedule.title": "Agenda",
|
|
1290
1520
|
"crud.archetype.schedule.description": "Visualize e gerencie compromissos, turnos e disponibilidade desta pessoa.",
|
|
1291
1521
|
"crud.archetype.inventory.title": "Estoque",
|
package/src/index.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
// Types
|
|
2
2
|
export type { EntityArchetype, BaseEntity, PersonEntity, ProductEntity, ServiceEntity, OrderEntity, TransactionEntity, LocationEntity, ScheduleEntity, CategoryEntity } from './types/entities'
|
|
3
|
-
export type { EntityDef, FieldDef, FieldType, FieldRelation, FieldGroup, DetailTab, FormLayout, ComputedFieldValue } from './types/crud'
|
|
4
|
-
export type { PluginManifest, PluginScope, PluginStatus, ResolvedPluginManifest, PluginRuntime, PluginRuntimeContext, PluginWidgetZone, PluginWidgetDefinition, PluginNavigationEntry, PluginSettingsTab, PluginRouteDefinition, PluginAITool, PluginRegistryDef, PluginMigration, PluginDiagnostic, VerticalId, ScaffoldType, TenantPluginBinding, DashboardWidgetDef, DashboardWidgetKind, DashboardSurface, DashboardLayoutConfig, ResolvedDashboardWidget } from './types/plugins'
|
|
3
|
+
export type { EntityDef, EntityImageConfig, FieldDef, FieldType, FieldRelation, FieldGroup, DetailTab, FormLayout, ComputedFieldValue } from './types/crud'
|
|
4
|
+
export type { PluginManifest, PluginScope, PluginStatus, ResolvedPluginManifest, PluginRuntime, PluginRuntimeContext, PluginWidgetZone, PluginWidgetDefinition, PluginNavigationEntry, PluginSettingsTab, PluginRouteDefinition, PluginAITool, AIToolMode, AIToolExecution, AgentRpcDeclaration, AIToolParameterProperty, AIToolParameters, AIToolSuggestion, PluginRegistryDef, PluginMigration, PluginDiagnostic, VerticalId, ScaffoldType, TenantPluginBinding, DashboardWidgetDef, DashboardWidgetKind, DashboardSurface, DashboardLayoutConfig, ResolvedDashboardWidget } from './types/plugins'
|
|
5
5
|
export { WidgetZone } from './types/plugins'
|
|
6
6
|
export type { AuthAdapter, AuthUser, AuthSession, AuthProvider } from './types/auth'
|
|
7
7
|
export type { OrgAdapter, Organization, OrgMember, OrgMembership, Location, Invite, CreateOrgOptions } from './types/org'
|
|
8
8
|
export type { PermissionsConfig, FeatureDeclaration, PermissionAction, PermissionProfile, SystemPermission } from './types/permissions'
|
|
9
|
-
export type { Plan, BillingConfig } from './types/billing'
|
|
9
|
+
export type { Plan, BillingConfig, BillingCheckoutFn } from './types/billing'
|
|
10
|
+
export type { PlanEntitlements, LimitDeclaration } from './types/entitlements'
|
|
10
11
|
export type { ThemeBrand, ThemeRadius, ThemeMode } from './types/theme'
|
|
11
12
|
export type { LocaleConfig } from './types/i18n'
|
|
12
13
|
|
|
13
14
|
// Data providers
|
|
14
15
|
export { createSupabaseProvider, setGlobalSupabaseClient, getSupabaseClientOptional } from './data/supabase'
|
|
16
|
+
export { countByTenant, invalidateCount } from './data/count'
|
|
17
|
+
export type { CountByTenantOptions } from './data/count'
|
|
15
18
|
export { getFayzCloudClient, FAYZ_CLOUD_URL, FAYZ_CLOUD_PUBLISHABLE_KEY } from './data/cloud'
|
|
16
19
|
export { createFayzApiProvider } from './data/platform-api'
|
|
17
20
|
export { createMockProvider } from './data/mock'
|
|
@@ -23,6 +26,12 @@ export {
|
|
|
23
26
|
COUNTRIES, DEFAULT_COUNTRY, getCountry, unmaskPhone, maskPhone, maskDigitCount,
|
|
24
27
|
} from './phone'
|
|
25
28
|
export type { CountryDef } from './phone'
|
|
29
|
+
export {
|
|
30
|
+
normalizePostalCode, formatPostalCode, isValidPostalCode,
|
|
31
|
+
createViaCepProvider, setPostalLookupProvider, getPostalLookupProvider,
|
|
32
|
+
lookupPostalCode, clearPostalCache,
|
|
33
|
+
} from './address'
|
|
34
|
+
export type { PostalAddress, PostalLookupProvider } from './address'
|
|
26
35
|
export type {
|
|
27
36
|
PaymentProvider, PaymentMethod, ChargeStatus, CreateChargeInput, PixCharge,
|
|
28
37
|
} from './types/payment'
|
|
@@ -148,7 +157,13 @@ export type {
|
|
|
148
157
|
PageManifest,
|
|
149
158
|
PluginRef,
|
|
150
159
|
ManifestMigration,
|
|
160
|
+
AgentContract,
|
|
161
|
+
AgentDomainKnowledge,
|
|
162
|
+
EntityContract,
|
|
163
|
+
AIToolContract,
|
|
164
|
+
RpcContract,
|
|
151
165
|
} from './manifest'
|
|
166
|
+
export { serializeManifest, computeContractHash, serializeManifestWithHash } from './manifest/serialize'
|
|
152
167
|
export { default as appManifestSchema } from './manifest/app-manifest.schema.json'
|
|
153
168
|
|
|
154
169
|
// App entry points (manifest → running app)
|