tapimo 0.4.1 → 0.4.3

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.
Files changed (243) hide show
  1. package/README.md +1 -1
  2. package/dist/ApiKey.d.ts +1 -9
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +2 -3
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +53 -27
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +32 -17
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +2451 -1533
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +23 -9
  13. package/dist/App.js.map +1 -1
  14. package/dist/Assets.d.ts +10 -0
  15. package/dist/Assets.d.ts.map +1 -1
  16. package/dist/Assets.js +4 -0
  17. package/dist/Assets.js.map +1 -1
  18. package/dist/Auth.d.ts +2 -1
  19. package/dist/Auth.d.ts.map +1 -1
  20. package/dist/Auth.js +2 -1
  21. package/dist/Auth.js.map +1 -1
  22. package/dist/Scope.d.ts +40 -15
  23. package/dist/Scope.d.ts.map +1 -1
  24. package/dist/Scope.js +44 -13
  25. package/dist/Scope.js.map +1 -1
  26. package/dist/admin/App.d.ts +560 -9
  27. package/dist/admin/App.d.ts.map +1 -1
  28. package/dist/admin/App.js +38 -6
  29. package/dist/admin/App.js.map +1 -1
  30. package/dist/admin/apps/api-keys.d.ts +10 -24
  31. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  32. package/dist/admin/apps/api-keys.js +10 -3
  33. package/dist/admin/apps/api-keys.js.map +1 -1
  34. package/dist/admin/apps/organizations.d.ts +833 -0
  35. package/dist/admin/apps/organizations.d.ts.map +1 -0
  36. package/dist/admin/apps/organizations.js +733 -0
  37. package/dist/admin/apps/organizations.js.map +1 -0
  38. package/dist/admin/apps/scopes.d.ts +8 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/ui.generated.d.ts +1 -1
  41. package/dist/admin/ui.generated.d.ts.map +1 -1
  42. package/dist/admin/ui.generated.js +53 -13
  43. package/dist/admin/ui.generated.js.map +1 -1
  44. package/dist/analytics/Analytics.d.ts +10 -0
  45. package/dist/analytics/Analytics.d.ts.map +1 -1
  46. package/dist/analytics/Analytics.js +8 -0
  47. package/dist/analytics/Analytics.js.map +1 -1
  48. package/dist/analytics/tables/requestEvents.d.ts +9 -0
  49. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  50. package/dist/analytics/tables/requestEvents.js +14 -0
  51. package/dist/analytics/tables/requestEvents.js.map +1 -1
  52. package/dist/apps/data/App.d.ts +1354 -984
  53. package/dist/apps/data/App.d.ts.map +1 -1
  54. package/dist/apps/data/FundingRouteProviders.d.ts +94 -5
  55. package/dist/apps/data/FundingRouteProviders.d.ts.map +1 -1
  56. package/dist/apps/data/FundingRouteProviders.js +196 -5
  57. package/dist/apps/data/FundingRouteProviders.js.map +1 -1
  58. package/dist/apps/data/routes/activities.d.ts +38 -38
  59. package/dist/apps/data/routes/addresses.d.ts +24 -24
  60. package/dist/apps/data/routes/blocks.d.ts +39 -39
  61. package/dist/apps/data/routes/exchanges.d.ts +453 -453
  62. package/dist/apps/data/routes/fee-amm.d.ts +101 -101
  63. package/dist/apps/data/routes/funding.d.ts +730 -138
  64. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  65. package/dist/apps/data/routes/funding.js +234 -0
  66. package/dist/apps/data/routes/funding.js.map +1 -1
  67. package/dist/apps/data/routes/indexer.d.ts +5 -5
  68. package/dist/apps/data/routes/transactions.d.ts +281 -281
  69. package/dist/apps/data/routes/transfers.d.ts +58 -58
  70. package/dist/apps/data/routes/verified-tokens.d.ts +81 -81
  71. package/dist/apps/management/App.d.ts +1156 -615
  72. package/dist/apps/management/App.d.ts.map +1 -1
  73. package/dist/apps/management/App.js +2 -2
  74. package/dist/apps/management/Billing.d.ts +3 -0
  75. package/dist/apps/management/Billing.d.ts.map +1 -1
  76. package/dist/apps/management/Billing.js +7 -2
  77. package/dist/apps/management/Billing.js.map +1 -1
  78. package/dist/apps/management/routes/api-keys.d.ts +106 -90
  79. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  80. package/dist/apps/management/routes/api-keys.js +41 -30
  81. package/dist/apps/management/routes/api-keys.js.map +1 -1
  82. package/dist/apps/management/routes/billing.d.ts +278 -146
  83. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  84. package/dist/apps/management/routes/billing.js +14 -14
  85. package/dist/apps/management/routes/billing.js.map +1 -1
  86. package/dist/apps/management/routes/invitations.d.ts +93 -20
  87. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  88. package/dist/apps/management/routes/invitations.js +12 -15
  89. package/dist/apps/management/routes/invitations.js.map +1 -1
  90. package/dist/apps/management/routes/me.d.ts +6 -6
  91. package/dist/apps/management/routes/members.d.ts +89 -18
  92. package/dist/apps/management/routes/members.d.ts.map +1 -1
  93. package/dist/apps/management/routes/members.js +10 -10
  94. package/dist/apps/management/routes/members.js.map +1 -1
  95. package/dist/apps/management/routes/orgs.d.ts +130 -70
  96. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  97. package/dist/apps/management/routes/orgs.js +19 -19
  98. package/dist/apps/management/routes/orgs.js.map +1 -1
  99. package/dist/apps/management/routes/projects.d.ts +137 -48
  100. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  101. package/dist/apps/management/routes/projects.js +12 -13
  102. package/dist/apps/management/routes/projects.js.map +1 -1
  103. package/dist/apps/management/routes/scopes.d.ts +4 -18
  104. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  105. package/dist/apps/management/routes/scopes.js +6 -7
  106. package/dist/apps/management/routes/scopes.js.map +1 -1
  107. package/dist/apps/management/routes/usage.d.ts +63 -19
  108. package/dist/apps/management/routes/usage.d.ts.map +1 -1
  109. package/dist/apps/management/routes/usage.js +9 -10
  110. package/dist/apps/management/routes/usage.js.map +1 -1
  111. package/dist/apps/mcp.d.ts.map +1 -1
  112. package/dist/apps/mcp.js +35 -3
  113. package/dist/apps/mcp.js.map +1 -1
  114. package/dist/apps/relay/App.d.ts +26 -4
  115. package/dist/apps/relay/App.d.ts.map +1 -1
  116. package/dist/apps/relay/App.js +17 -14
  117. package/dist/apps/relay/App.js.map +1 -1
  118. package/dist/db/Db.d.ts +5 -3
  119. package/dist/db/Db.d.ts.map +1 -1
  120. package/dist/db/Db.js +52 -0
  121. package/dist/db/Db.js.map +1 -1
  122. package/dist/db/Schema.d.ts +13 -0
  123. package/dist/db/Schema.d.ts.map +1 -1
  124. package/dist/db/Schema.js +16 -1
  125. package/dist/db/Schema.js.map +1 -1
  126. package/dist/db/tables/adminAuditLogs.d.ts +27 -0
  127. package/dist/db/tables/adminAuditLogs.d.ts.map +1 -0
  128. package/dist/db/tables/adminAuditLogs.js +19 -0
  129. package/dist/db/tables/adminAuditLogs.js.map +1 -0
  130. package/dist/db/tables/invitations.d.ts +1 -1
  131. package/dist/db/tables/invitations.d.ts.map +1 -1
  132. package/dist/db/tables/memberships.d.ts +32 -4
  133. package/dist/db/tables/memberships.d.ts.map +1 -1
  134. package/dist/db/tables/memberships.js +31 -0
  135. package/dist/db/tables/memberships.js.map +1 -1
  136. package/dist/db/tables/organizations.d.ts +43 -1
  137. package/dist/db/tables/organizations.d.ts.map +1 -1
  138. package/dist/db/tables/organizations.js +103 -0
  139. package/dist/db/tables/organizations.js.map +1 -1
  140. package/dist/db/tables/projects.d.ts +25 -0
  141. package/dist/db/tables/projects.d.ts.map +1 -1
  142. package/dist/db/tables/projects.js +22 -0
  143. package/dist/db/tables/projects.js.map +1 -1
  144. package/dist/db/tables/webhookDeliveries.d.ts +26 -0
  145. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -1
  146. package/dist/db/tables/webhookDeliveries.js +37 -0
  147. package/dist/db/tables/webhookDeliveries.js.map +1 -1
  148. package/dist/internal/Auth.d.ts +13 -18
  149. package/dist/internal/Auth.d.ts.map +1 -1
  150. package/dist/internal/Auth.js +78 -58
  151. package/dist/internal/Auth.js.map +1 -1
  152. package/dist/internal/Cursor.d.ts +1 -1
  153. package/dist/internal/Cursor.d.ts.map +1 -1
  154. package/dist/internal/Cursor.js +4 -0
  155. package/dist/internal/Cursor.js.map +1 -1
  156. package/dist/internal/EdgeCache.d.ts.map +1 -1
  157. package/dist/internal/EdgeCache.js +2 -0
  158. package/dist/internal/EdgeCache.js.map +1 -1
  159. package/dist/internal/FundingRoutes.d.ts +170 -0
  160. package/dist/internal/FundingRoutes.d.ts.map +1 -1
  161. package/dist/internal/FundingRoutes.js +68 -6
  162. package/dist/internal/FundingRoutes.js.map +1 -1
  163. package/dist/internal/Log.d.ts +16 -1
  164. package/dist/internal/Log.d.ts.map +1 -1
  165. package/dist/internal/Log.js +5 -3
  166. package/dist/internal/Log.js.map +1 -1
  167. package/package.json +2 -2
  168. package/src/ApiKey.ts +2 -3
  169. package/src/ApiKeys.ts +95 -17
  170. package/src/App.test.ts +44 -2
  171. package/src/App.ts +29 -9
  172. package/src/Assets.ts +8 -0
  173. package/src/Auth.test-d.ts +36 -0
  174. package/src/Auth.ts +20 -1
  175. package/src/Client.test-d.ts +64 -17
  176. package/src/Scope.test.ts +34 -0
  177. package/src/Scope.ts +53 -17
  178. package/src/admin/App.test.ts +33 -3
  179. package/src/admin/App.ts +49 -14
  180. package/src/admin/apps/api-keys.ts +10 -3
  181. package/src/admin/apps/organizations.test.ts +455 -0
  182. package/src/admin/apps/organizations.ts +953 -0
  183. package/src/admin/apps/scopes.test.ts +10 -0
  184. package/src/admin/ui/plugin.ts +1 -1
  185. package/src/admin/ui/src/components/MobileProfile.tsx +2 -0
  186. package/src/admin/ui/src/components/ThemeToggle.tsx +20 -0
  187. package/src/admin/ui/src/lib/api-keys.ts +36 -19
  188. package/src/admin/ui/src/lib/config.ts +2 -3
  189. package/src/admin/ui/src/lib/organizations.test.ts +89 -0
  190. package/src/admin/ui/src/lib/organizations.ts +262 -0
  191. package/src/admin/ui/src/lib/query-client.ts +3 -1
  192. package/src/admin/ui/src/lib/theme.ts +57 -0
  193. package/src/admin/ui/src/routeTree.gen.ts +47 -4
  194. package/src/admin/ui/src/routes/__root.tsx +18 -3
  195. package/src/admin/ui/src/routes/api-keys.tsx +52 -7
  196. package/src/admin/ui/src/routes/index.tsx +1 -1
  197. package/src/admin/ui/src/routes/organizations.tsx +158 -0
  198. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +710 -0
  199. package/src/admin/ui/src/start.ts +6 -0
  200. package/src/admin/ui.generated.ts +63 -13
  201. package/src/analytics/Analytics.ts +25 -0
  202. package/src/analytics/tables/requestEvents.ts +33 -0
  203. package/src/apps/composition.test.ts +82 -2
  204. package/src/apps/data/FundingRouteProviders.test.ts +204 -0
  205. package/src/apps/data/FundingRouteProviders.ts +294 -9
  206. package/src/apps/data/routes/funding.test.ts +90 -23
  207. package/src/apps/data/routes/funding.ts +315 -0
  208. package/src/apps/management/App.ts +2 -2
  209. package/src/apps/management/Billing.ts +10 -2
  210. package/src/apps/management/routes/api-keys.test.ts +137 -1
  211. package/src/apps/management/routes/api-keys.ts +63 -38
  212. package/src/apps/management/routes/billing.ts +14 -14
  213. package/src/apps/management/routes/invitations.test.ts +102 -1
  214. package/src/apps/management/routes/invitations.ts +13 -15
  215. package/src/apps/management/routes/members.ts +10 -10
  216. package/src/apps/management/routes/orgs.test.ts +78 -16
  217. package/src/apps/management/routes/orgs.ts +21 -20
  218. package/src/apps/management/routes/projects.ts +12 -13
  219. package/src/apps/management/routes/scopes.test.ts +16 -0
  220. package/src/apps/management/routes/scopes.ts +6 -7
  221. package/src/apps/management/routes/usage.ts +9 -14
  222. package/src/apps/mcp.test.ts +25 -1
  223. package/src/apps/mcp.ts +43 -3
  224. package/src/apps/relay/App.test.ts +23 -18
  225. package/src/apps/relay/App.ts +35 -14
  226. package/src/db/Db.ts +59 -3
  227. package/src/db/Schema.ts +20 -1
  228. package/src/db/tables/adminAuditLogs.ts +47 -0
  229. package/src/db/tables/invitations.ts +1 -1
  230. package/src/db/tables/memberships.ts +59 -3
  231. package/src/db/tables/organizations.ts +148 -1
  232. package/src/db/tables/projects.ts +47 -0
  233. package/src/db/tables/webhookDeliveries.ts +83 -0
  234. package/src/internal/Auth.test.ts +174 -7
  235. package/src/internal/Auth.ts +90 -62
  236. package/src/internal/Cursor.test.ts +10 -0
  237. package/src/internal/Cursor.ts +5 -1
  238. package/src/internal/EdgeCache.test.ts +11 -0
  239. package/src/internal/EdgeCache.ts +2 -0
  240. package/src/internal/FundingRoutes.test.ts +26 -0
  241. package/src/internal/FundingRoutes.ts +187 -18
  242. package/src/internal/Log.test.ts +41 -3
  243. package/src/internal/Log.ts +23 -4
@@ -10,14 +10,21 @@
10
10
 
11
11
  import { Route as rootRouteImport } from './routes/__root'
12
12
  import { Route as VerifiedTokensRouteImport } from './routes/verified-tokens'
13
+ import { Route as OrganizationsRouteImport } from './routes/organizations'
13
14
  import { Route as ApiKeysRouteImport } from './routes/api-keys'
14
15
  import { Route as IndexRouteImport } from './routes/index'
16
+ import { Route as OrganizationsOrgIdRouteImport } from './routes/organizations_.$orgId'
15
17
 
16
18
  const VerifiedTokensRoute = VerifiedTokensRouteImport.update({
17
19
  id: '/verified-tokens',
18
20
  path: '/verified-tokens',
19
21
  getParentRoute: () => rootRouteImport,
20
22
  } as any)
23
+ const OrganizationsRoute = OrganizationsRouteImport.update({
24
+ id: '/organizations',
25
+ path: '/organizations',
26
+ getParentRoute: () => rootRouteImport,
27
+ } as any)
21
28
  const ApiKeysRoute = ApiKeysRouteImport.update({
22
29
  id: '/api-keys',
23
30
  path: '/api-keys',
@@ -28,35 +35,54 @@ const IndexRoute = IndexRouteImport.update({
28
35
  path: '/',
29
36
  getParentRoute: () => rootRouteImport,
30
37
  } as any)
38
+ const OrganizationsOrgIdRoute = OrganizationsOrgIdRouteImport.update({
39
+ id: '/organizations_/$orgId',
40
+ path: '/organizations/$orgId',
41
+ getParentRoute: () => rootRouteImport,
42
+ } as any)
31
43
 
32
44
  export interface FileRoutesByFullPath {
33
45
  '/': typeof IndexRoute
34
46
  '/api-keys': typeof ApiKeysRoute
47
+ '/organizations': typeof OrganizationsRoute
35
48
  '/verified-tokens': typeof VerifiedTokensRoute
49
+ '/organizations/$orgId': typeof OrganizationsOrgIdRoute
36
50
  }
37
51
  export interface FileRoutesByTo {
38
52
  '/': typeof IndexRoute
39
53
  '/api-keys': typeof ApiKeysRoute
54
+ '/organizations': typeof OrganizationsRoute
40
55
  '/verified-tokens': typeof VerifiedTokensRoute
56
+ '/organizations/$orgId': typeof OrganizationsOrgIdRoute
41
57
  }
42
58
  export interface FileRoutesById {
43
59
  __root__: typeof rootRouteImport
44
60
  '/': typeof IndexRoute
45
61
  '/api-keys': typeof ApiKeysRoute
62
+ '/organizations': typeof OrganizationsRoute
46
63
  '/verified-tokens': typeof VerifiedTokensRoute
64
+ '/organizations_/$orgId': typeof OrganizationsOrgIdRoute
47
65
  }
48
66
  export interface FileRouteTypes {
49
67
  fileRoutesByFullPath: FileRoutesByFullPath
50
- fullPaths: '/' | '/api-keys' | '/verified-tokens'
68
+ fullPaths: '/' | '/api-keys' | '/organizations' | '/verified-tokens' | '/organizations/$orgId'
51
69
  fileRoutesByTo: FileRoutesByTo
52
- to: '/' | '/api-keys' | '/verified-tokens'
53
- id: '__root__' | '/' | '/api-keys' | '/verified-tokens'
70
+ to: '/' | '/api-keys' | '/organizations' | '/verified-tokens' | '/organizations/$orgId'
71
+ id:
72
+ | '__root__'
73
+ | '/'
74
+ | '/api-keys'
75
+ | '/organizations'
76
+ | '/verified-tokens'
77
+ | '/organizations_/$orgId'
54
78
  fileRoutesById: FileRoutesById
55
79
  }
56
80
  export interface RootRouteChildren {
57
81
  IndexRoute: typeof IndexRoute
58
82
  ApiKeysRoute: typeof ApiKeysRoute
83
+ OrganizationsRoute: typeof OrganizationsRoute
59
84
  VerifiedTokensRoute: typeof VerifiedTokensRoute
85
+ OrganizationsOrgIdRoute: typeof OrganizationsOrgIdRoute
60
86
  }
61
87
 
62
88
  declare module '@tanstack/react-router' {
@@ -68,6 +94,13 @@ declare module '@tanstack/react-router' {
68
94
  preLoaderRoute: typeof VerifiedTokensRouteImport
69
95
  parentRoute: typeof rootRouteImport
70
96
  }
97
+ '/organizations': {
98
+ id: '/organizations'
99
+ path: '/organizations'
100
+ fullPath: '/organizations'
101
+ preLoaderRoute: typeof OrganizationsRouteImport
102
+ parentRoute: typeof rootRouteImport
103
+ }
71
104
  '/api-keys': {
72
105
  id: '/api-keys'
73
106
  path: '/api-keys'
@@ -82,23 +115,33 @@ declare module '@tanstack/react-router' {
82
115
  preLoaderRoute: typeof IndexRouteImport
83
116
  parentRoute: typeof rootRouteImport
84
117
  }
118
+ '/organizations_/$orgId': {
119
+ id: '/organizations_/$orgId'
120
+ path: '/organizations/$orgId'
121
+ fullPath: '/organizations/$orgId'
122
+ preLoaderRoute: typeof OrganizationsOrgIdRouteImport
123
+ parentRoute: typeof rootRouteImport
124
+ }
85
125
  }
86
126
  }
87
127
 
88
128
  const rootRouteChildren: RootRouteChildren = {
89
129
  IndexRoute: IndexRoute,
90
130
  ApiKeysRoute: ApiKeysRoute,
131
+ OrganizationsRoute: OrganizationsRoute,
91
132
  VerifiedTokensRoute: VerifiedTokensRoute,
133
+ OrganizationsOrgIdRoute: OrganizationsOrgIdRoute,
92
134
  }
93
135
  export const routeTree = rootRouteImport
94
136
  ._addFileChildren(rootRouteChildren)
95
137
  ._addFileTypes<FileRouteTypes>()
96
138
 
97
139
  import type { getRouter } from './router.tsx'
98
- import type { createStart } from '@tanstack/react-start'
140
+ import type { startInstance } from './start.ts'
99
141
  declare module '@tanstack/react-start' {
100
142
  interface Register {
101
143
  ssr: true
102
144
  router: Awaited<ReturnType<typeof getRouter>>
145
+ config: Awaited<ReturnType<typeof startInstance.getOptions>>
103
146
  }
104
147
  }
@@ -10,14 +10,16 @@ import type { QueryClient } from '@tanstack/react-query'
10
10
  import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
11
11
  import { type ComponentType } from 'react'
12
12
  import { Button, Identicon } from 'regen-ui'
13
- import { BadgeCheck, KeyRound, LogOut } from 'lucide-react'
13
+ import { BadgeCheck, Building2, KeyRound, LogOut } from 'lucide-react'
14
14
  import { WagmiProvider } from 'wagmi'
15
15
 
16
16
  import appCss from '../styles.css?url'
17
17
  import { TempoLogo, TempoMark } from '../components/TempoLogo'
18
18
  import { identiconAddress } from '../components/MobileProfile'
19
+ import { ThemeToggle } from '../components/ThemeToggle'
19
20
  import * as Config from '../lib/config'
20
21
  import { persistOptions, queryClient } from '../lib/query-client'
22
+ import * as Theme from '../lib/theme'
21
23
  import { config as wagmiConfig } from '../lib/wagmi'
22
24
 
23
25
  export type RouterContext = { queryClient: QueryClient }
@@ -37,6 +39,7 @@ export const Route = createRootRouteWithContext<RouterContext>()({
37
39
  { content: 'width=device-width, initial-scale=1', name: 'viewport' },
38
40
  { title: 'Tempo API Admin' },
39
41
  ],
42
+ scripts: [{ children: Theme.bootstrap }],
40
43
  }),
41
44
  shellComponent: RootDocument,
42
45
  component: AdminShell,
@@ -45,8 +48,14 @@ export const Route = createRootRouteWithContext<RouterContext>()({
45
48
 
46
49
  function RootDocument({ children }: { children: React.ReactNode }) {
47
50
  return (
48
- <html lang="en" data-theme="dark" data-regen-radius="none">
51
+ <html lang="en" data-regen-radius="none" suppressHydrationWarning>
49
52
  <head>
53
+ <base data-tempo-base="true" href="/" suppressHydrationWarning />
54
+ <script
55
+ data-tempo-config="true"
56
+ suppressHydrationWarning
57
+ dangerouslySetInnerHTML={{ __html: 'window.__TEMPO_API_CONFIG__=undefined' }}
58
+ />
50
59
  <HeadContent />
51
60
  </head>
52
61
  <body>
@@ -103,6 +112,7 @@ function Admin() {
103
112
  }
104
113
 
105
114
  const nav = [
115
+ { exact: false, href: '/organizations', icon: Building2, label: 'Organizations' },
106
116
  { exact: false, href: '/api-keys', icon: KeyRound, label: 'API Keys' },
107
117
  { exact: false, href: '/verified-tokens', icon: BadgeCheck, label: 'Verified Tokens' },
108
118
  ] as const
@@ -129,7 +139,12 @@ function Sidebar(props: { email: string; logoutUrl?: string }) {
129
139
  />
130
140
  ))}
131
141
  </nav>
132
- <ProfileCard email={props.email} logoutUrl={props.logoutUrl} />
142
+ <div className="flex w-full flex-col gap-1">
143
+ <div className="flex justify-start px-2.5 max-lg:justify-center max-lg:px-2">
144
+ <ThemeToggle />
145
+ </div>
146
+ <ProfileCard email={props.email} logoutUrl={props.logoutUrl} />
147
+ </div>
133
148
  </aside>
134
149
  )
135
150
  }
@@ -10,18 +10,21 @@ import * as ApiKeys from '../lib/api-keys'
10
10
  import * as Scopes from '../lib/scopes'
11
11
 
12
12
  export const Route = createFileRoute('/api-keys')({
13
- loader: ({ context }) =>
13
+ validateSearch: ApiKeys.search,
14
+ loaderDeps: ({ search }) => search,
15
+ loader: ({ context, deps }) =>
14
16
  Promise.all([
15
- context.queryClient.prefetchQuery(ApiKeys.get.options()),
17
+ context.queryClient.prefetchQuery(ApiKeys.get.options(deps)),
16
18
  context.queryClient.prefetchQuery(Scopes.get.options()),
17
19
  ]),
18
20
  component: RouteComponent,
19
21
  })
20
22
 
21
23
  function RouteComponent() {
24
+ const filters = Route.useSearch()
22
25
  const [open, setOpen] = useState(false)
23
26
  const [environment, setEnvironment] = useState<'production' | 'sandbox'>('production')
24
- const keys = ApiKeys.get.useQuery()
27
+ const keys = ApiKeys.get.useQuery(filters)
25
28
  const provision = ApiKeys.provision.useMutation()
26
29
 
27
30
  function onSubmit(form: FormData) {
@@ -42,8 +45,10 @@ function RouteComponent() {
42
45
  {
43
46
  environment,
44
47
  scopes: scopes as ApiKeys.provision.Input['scopes'],
48
+ ...(filters.orgId ? { orgId: filters.orgId } : {}),
45
49
  ...(name ? { name } : {}),
46
50
  ...(perMinute === undefined ? {} : { rateLimits: { '*': { perMinute } } }),
51
+ ...(filters.projectId ? { projectId: filters.projectId } : {}),
47
52
  },
48
53
  { onError: (error) => alert(error.message), onSuccess: () => setOpen(false) },
49
54
  )
@@ -53,7 +58,13 @@ function RouteComponent() {
53
58
  <Dialog.Root onOpenChange={setOpen} open={open}>
54
59
  <Page
55
60
  title="API Keys"
56
- description="Manage keys that authenticate requests to the API"
61
+ description={
62
+ filters.projectId
63
+ ? `Keys for project ${filters.projectId}`
64
+ : filters.orgId
65
+ ? `Keys for organization ${filters.orgId}`
66
+ : 'Manage keys that authenticate requests to the API'
67
+ }
57
68
  action={
58
69
  <>
59
70
  <Select
@@ -88,7 +99,12 @@ function RouteComponent() {
88
99
  loading={keys.isPending}
89
100
  />
90
101
  <Dialog.Popup>
91
- <ProvisionForm onSubmit={onSubmit} pending={provision.isPending} />
102
+ <ProvisionForm
103
+ onSubmit={onSubmit}
104
+ orgId={filters.orgId}
105
+ pending={provision.isPending}
106
+ projectId={filters.projectId}
107
+ />
92
108
  </Dialog.Popup>
93
109
  </Page>
94
110
  </Dialog.Root>
@@ -132,12 +148,26 @@ function TokenBanner(props: { onDismiss: () => void; token: string }) {
132
148
  )
133
149
  }
134
150
 
135
- function ProvisionForm(props: { onSubmit: (form: FormData) => void; pending: boolean }) {
151
+ function ProvisionForm(props: {
152
+ onSubmit: (form: FormData) => void
153
+ orgId?: string | undefined
154
+ pending: boolean
155
+ projectId?: string | undefined
156
+ }) {
136
157
  const scopes = Scopes.get.useQuery()
158
+ const destination = props.projectId
159
+ ? { label: 'Project', value: props.projectId }
160
+ : props.orgId
161
+ ? { label: 'Organization', value: props.orgId }
162
+ : undefined
137
163
  return (
138
164
  <>
139
165
  <Dialog.Title>Provision a Key</Dialog.Title>
140
- <Dialog.Description>Create a key that authenticates requests to the API</Dialog.Description>
166
+ <Dialog.Description>
167
+ {destination
168
+ ? `Create an API key owned by this ${destination.label.toLowerCase()}.`
169
+ : 'Create a key that authenticates requests to the API.'}
170
+ </Dialog.Description>
141
171
  <form
142
172
  className="mt-6 flex flex-col gap-8"
143
173
  onSubmit={(event) => {
@@ -145,6 +175,21 @@ function ProvisionForm(props: { onSubmit: (form: FormData) => void; pending: boo
145
175
  props.onSubmit(new FormData(event.currentTarget))
146
176
  }}
147
177
  >
178
+ {destination ? (
179
+ <div className="border border-border bg-secondary p-3">
180
+ <p className="text-label-12 text-foreground-secondary">
181
+ Destination {destination.label}
182
+ </p>
183
+ <p className="mt-1 truncate font-mono text-label-13 text-foreground">
184
+ {destination.value}
185
+ </p>
186
+ {props.projectId && props.orgId ? (
187
+ <p className="mt-1 truncate font-mono text-label-12 text-foreground-secondary">
188
+ Organization {props.orgId}
189
+ </p>
190
+ ) : null}
191
+ </div>
192
+ ) : null}
148
193
  <Fields className="gap-6" variant="default">
149
194
  <Input label="Name" name="name" placeholder="e.g. production-api" />
150
195
  <div className="flex flex-col gap-2">
@@ -2,6 +2,6 @@ import { createFileRoute, redirect } from '@tanstack/react-router'
2
2
 
3
3
  export const Route = createFileRoute('/')({
4
4
  beforeLoad: () => {
5
- throw redirect({ to: '/api-keys' })
5
+ throw redirect({ to: '/organizations' })
6
6
  },
7
7
  })
@@ -0,0 +1,158 @@
1
+ import { useSuspenseInfiniteQuery } from '@tanstack/react-query'
2
+ import {
3
+ createFileRoute,
4
+ type ErrorComponentProps,
5
+ Link,
6
+ useNavigate,
7
+ useRouter,
8
+ } from '@tanstack/react-router'
9
+ import { useEffect, useState } from 'react'
10
+ import { Button, Input, Rows } from 'regen-ui'
11
+ import { ChevronRight, Search } from 'lucide-react'
12
+
13
+ import { Card, Notice, Page } from '../components/Page'
14
+ import * as Organizations from '../lib/organizations'
15
+
16
+ export const Route = createFileRoute('/organizations')({
17
+ validateSearch: Organizations.schema.Search,
18
+ loaderDeps: ({ search }) => ({ query: search.query || undefined }),
19
+ loader: ({ context, deps }) =>
20
+ context.queryClient.ensureInfiniteQueryData(Organizations.list.options(deps)),
21
+ component: PageComponent,
22
+ errorComponent: LookupError,
23
+ })
24
+
25
+ function PageComponent() {
26
+ const { query } = Route.useLoaderDeps()
27
+ const navigate = useNavigate({ from: Route.fullPath })
28
+ const [value, setValue] = useState(query ?? '')
29
+ const organizations = useSuspenseInfiniteQuery(Organizations.list.options({ query }))
30
+ const records = organizations.data.pages.flatMap((page) => page.data)
31
+ const invalid = value.trim().length > 0 && value.trim().length < 3
32
+
33
+ useEffect(() => setValue(query ?? ''), [query])
34
+
35
+ return (
36
+ <Page title="Organizations" description="Look up organizations by id, name, or member email">
37
+ <form
38
+ className="flex gap-2"
39
+ onSubmit={(event) => {
40
+ event.preventDefault()
41
+ const next = value.trim()
42
+ if (invalid) return
43
+ void navigate({
44
+ search: Organizations.searchParameters({ query: next }),
45
+ to: '/organizations',
46
+ })
47
+ }}
48
+ >
49
+ <div className="min-w-0 flex-1">
50
+ <Input
51
+ aria-label="Search organizations"
52
+ error={invalid ? 'Enter at least 3 characters.' : false}
53
+ onChange={(event) => setValue(event.target.value)}
54
+ placeholder="Organization id, user id, name, or member email"
55
+ value={value}
56
+ />
57
+ </div>
58
+ <Button disabled={invalid} type="submit">
59
+ <Search className="size-4" />
60
+ Search
61
+ </Button>
62
+ </form>
63
+
64
+ <Card>
65
+ {records.length === 0 ? (
66
+ <Notice>{query ? `No organizations match “${query}”.` : 'No organizations yet.'}</Notice>
67
+ ) : (
68
+ <Rows variant="surface">
69
+ {records.map((organization) => (
70
+ <Rows.Row
71
+ className="min-h-[64px] py-3"
72
+ key={organization.id}
73
+ render={
74
+ <Link
75
+ params={{ orgId: organization.id }}
76
+ search={Organizations.searchParameters({ query })}
77
+ to="/organizations/$orgId"
78
+ />
79
+ }
80
+ style={{ height: 'auto' }}
81
+ >
82
+ <span className="flex min-w-0 flex-1 flex-col gap-1">
83
+ <span className="truncate text-label-14 text-foreground">
84
+ {organization.name}
85
+ </span>
86
+ <span className="truncate font-mono text-label-12 text-foreground-secondary">
87
+ {organization.id}
88
+ </span>
89
+ </span>
90
+ <span className="hidden shrink-0 text-right sm:block">
91
+ <span className="block text-label-13 text-foreground">
92
+ {formatCount(organization.memberCount, 'member')}
93
+ </span>
94
+ <span className="block text-label-12 text-foreground-secondary">
95
+ {formatCount(organization.projectCount, 'project')}
96
+ </span>
97
+ </span>
98
+ <span className="hidden w-28 shrink-0 text-right text-label-12 text-foreground-secondary md:block">
99
+ {formatDate(organization.createdAt)}
100
+ </span>
101
+ <ChevronRight className="size-4 shrink-0 text-foreground-secondary" />
102
+ </Rows.Row>
103
+ ))}
104
+ </Rows>
105
+ )}
106
+ </Card>
107
+
108
+ {organizations.isFetchNextPageError ? (
109
+ <div
110
+ className="flex items-center justify-center gap-3 text-copy-14 text-negative"
111
+ role="alert"
112
+ >
113
+ <span>{organizations.error?.message ?? 'Failed to load more organizations.'}</span>
114
+ <Button onClick={() => organizations.fetchNextPage()}>Retry</Button>
115
+ </div>
116
+ ) : organizations.hasNextPage ? (
117
+ <Button
118
+ className="self-center"
119
+ loading={organizations.isFetchingNextPage}
120
+ onClick={() => organizations.fetchNextPage()}
121
+ >
122
+ Load more
123
+ </Button>
124
+ ) : null}
125
+ </Page>
126
+ )
127
+ }
128
+
129
+ function LookupError(options: LookupError.Props) {
130
+ const router = useRouter()
131
+ return (
132
+ <Page title="Organizations" description="Look up organizations by id, name, or member email">
133
+ <Card className="flex flex-col items-center gap-4 px-6 py-12 text-center">
134
+ <p className="text-copy-14 text-negative">{options.error.message}</p>
135
+ <Button
136
+ onClick={() => {
137
+ options.reset()
138
+ void router.invalidate()
139
+ }}
140
+ >
141
+ Try again
142
+ </Button>
143
+ </Card>
144
+ </Page>
145
+ )
146
+ }
147
+
148
+ declare namespace LookupError {
149
+ type Props = ErrorComponentProps
150
+ }
151
+
152
+ function formatCount(value: number, label: string) {
153
+ return `${value} ${label}${value === 1 ? '' : 's'}`
154
+ }
155
+
156
+ function formatDate(value: string) {
157
+ return new Intl.DateTimeFormat('en', { dateStyle: 'medium' }).format(new Date(value))
158
+ }