availsync 0.1.0

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 (460) hide show
  1. package/.adal/skills/stripe-best-practices/SKILL.md +42 -0
  2. package/.adal/skills/stripe-best-practices/references/billing.md +36 -0
  3. package/.adal/skills/stripe-best-practices/references/connect.md +48 -0
  4. package/.adal/skills/stripe-best-practices/references/payments.md +79 -0
  5. package/.adal/skills/stripe-best-practices/references/security.md +109 -0
  6. package/.adal/skills/stripe-best-practices/references/treasury.md +16 -0
  7. package/.adal/skills/stripe-projects/SKILL.md +139 -0
  8. package/.adal/skills/upgrade-stripe/SKILL.md +185 -0
  9. package/.agents/skills/stripe-best-practices/SKILL.md +42 -0
  10. package/.agents/skills/stripe-best-practices/references/billing.md +36 -0
  11. package/.agents/skills/stripe-best-practices/references/connect.md +48 -0
  12. package/.agents/skills/stripe-best-practices/references/payments.md +79 -0
  13. package/.agents/skills/stripe-best-practices/references/security.md +109 -0
  14. package/.agents/skills/stripe-best-practices/references/treasury.md +16 -0
  15. package/.agents/skills/stripe-projects/SKILL.md +139 -0
  16. package/.agents/skills/upgrade-stripe/SKILL.md +185 -0
  17. package/.augment/skills/stripe-best-practices/SKILL.md +42 -0
  18. package/.augment/skills/stripe-best-practices/references/billing.md +36 -0
  19. package/.augment/skills/stripe-best-practices/references/connect.md +48 -0
  20. package/.augment/skills/stripe-best-practices/references/payments.md +79 -0
  21. package/.augment/skills/stripe-best-practices/references/security.md +109 -0
  22. package/.augment/skills/stripe-best-practices/references/treasury.md +16 -0
  23. package/.augment/skills/stripe-projects/SKILL.md +139 -0
  24. package/.augment/skills/upgrade-stripe/SKILL.md +185 -0
  25. package/.bob/skills/stripe-best-practices/SKILL.md +42 -0
  26. package/.bob/skills/stripe-best-practices/references/billing.md +36 -0
  27. package/.bob/skills/stripe-best-practices/references/connect.md +48 -0
  28. package/.bob/skills/stripe-best-practices/references/payments.md +79 -0
  29. package/.bob/skills/stripe-best-practices/references/security.md +109 -0
  30. package/.bob/skills/stripe-best-practices/references/treasury.md +16 -0
  31. package/.bob/skills/stripe-projects/SKILL.md +139 -0
  32. package/.bob/skills/upgrade-stripe/SKILL.md +185 -0
  33. package/.claude/settings.local.json +7 -0
  34. package/.claude/skills/stripe-best-practices/SKILL.md +42 -0
  35. package/.claude/skills/stripe-best-practices/references/billing.md +36 -0
  36. package/.claude/skills/stripe-best-practices/references/connect.md +48 -0
  37. package/.claude/skills/stripe-best-practices/references/payments.md +79 -0
  38. package/.claude/skills/stripe-best-practices/references/security.md +109 -0
  39. package/.claude/skills/stripe-best-practices/references/treasury.md +16 -0
  40. package/.claude/skills/stripe-projects/SKILL.md +139 -0
  41. package/.claude/skills/upgrade-stripe/SKILL.md +185 -0
  42. package/.codebuddy/skills/stripe-best-practices/SKILL.md +42 -0
  43. package/.codebuddy/skills/stripe-best-practices/references/billing.md +36 -0
  44. package/.codebuddy/skills/stripe-best-practices/references/connect.md +48 -0
  45. package/.codebuddy/skills/stripe-best-practices/references/payments.md +79 -0
  46. package/.codebuddy/skills/stripe-best-practices/references/security.md +109 -0
  47. package/.codebuddy/skills/stripe-best-practices/references/treasury.md +16 -0
  48. package/.codebuddy/skills/stripe-projects/SKILL.md +139 -0
  49. package/.codebuddy/skills/upgrade-stripe/SKILL.md +185 -0
  50. package/.commandcode/skills/stripe-best-practices/SKILL.md +42 -0
  51. package/.commandcode/skills/stripe-best-practices/references/billing.md +36 -0
  52. package/.commandcode/skills/stripe-best-practices/references/connect.md +48 -0
  53. package/.commandcode/skills/stripe-best-practices/references/payments.md +79 -0
  54. package/.commandcode/skills/stripe-best-practices/references/security.md +109 -0
  55. package/.commandcode/skills/stripe-best-practices/references/treasury.md +16 -0
  56. package/.commandcode/skills/stripe-projects/SKILL.md +139 -0
  57. package/.commandcode/skills/upgrade-stripe/SKILL.md +185 -0
  58. package/.continue/skills/stripe-best-practices/SKILL.md +42 -0
  59. package/.continue/skills/stripe-best-practices/references/billing.md +36 -0
  60. package/.continue/skills/stripe-best-practices/references/connect.md +48 -0
  61. package/.continue/skills/stripe-best-practices/references/payments.md +79 -0
  62. package/.continue/skills/stripe-best-practices/references/security.md +109 -0
  63. package/.continue/skills/stripe-best-practices/references/treasury.md +16 -0
  64. package/.continue/skills/stripe-projects/SKILL.md +139 -0
  65. package/.continue/skills/upgrade-stripe/SKILL.md +185 -0
  66. package/.cortex/skills/stripe-best-practices/SKILL.md +42 -0
  67. package/.cortex/skills/stripe-best-practices/references/billing.md +36 -0
  68. package/.cortex/skills/stripe-best-practices/references/connect.md +48 -0
  69. package/.cortex/skills/stripe-best-practices/references/payments.md +79 -0
  70. package/.cortex/skills/stripe-best-practices/references/security.md +109 -0
  71. package/.cortex/skills/stripe-best-practices/references/treasury.md +16 -0
  72. package/.cortex/skills/stripe-projects/SKILL.md +139 -0
  73. package/.cortex/skills/upgrade-stripe/SKILL.md +185 -0
  74. package/.crush/skills/stripe-best-practices/SKILL.md +42 -0
  75. package/.crush/skills/stripe-best-practices/references/billing.md +36 -0
  76. package/.crush/skills/stripe-best-practices/references/connect.md +48 -0
  77. package/.crush/skills/stripe-best-practices/references/payments.md +79 -0
  78. package/.crush/skills/stripe-best-practices/references/security.md +109 -0
  79. package/.crush/skills/stripe-best-practices/references/treasury.md +16 -0
  80. package/.crush/skills/stripe-projects/SKILL.md +139 -0
  81. package/.crush/skills/upgrade-stripe/SKILL.md +185 -0
  82. package/.env.example +20 -0
  83. package/.factory/skills/stripe-best-practices/SKILL.md +42 -0
  84. package/.factory/skills/stripe-best-practices/references/billing.md +36 -0
  85. package/.factory/skills/stripe-best-practices/references/connect.md +48 -0
  86. package/.factory/skills/stripe-best-practices/references/payments.md +79 -0
  87. package/.factory/skills/stripe-best-practices/references/security.md +109 -0
  88. package/.factory/skills/stripe-best-practices/references/treasury.md +16 -0
  89. package/.factory/skills/stripe-projects/SKILL.md +139 -0
  90. package/.factory/skills/upgrade-stripe/SKILL.md +185 -0
  91. package/.goose/skills/stripe-best-practices/SKILL.md +42 -0
  92. package/.goose/skills/stripe-best-practices/references/billing.md +36 -0
  93. package/.goose/skills/stripe-best-practices/references/connect.md +48 -0
  94. package/.goose/skills/stripe-best-practices/references/payments.md +79 -0
  95. package/.goose/skills/stripe-best-practices/references/security.md +109 -0
  96. package/.goose/skills/stripe-best-practices/references/treasury.md +16 -0
  97. package/.goose/skills/stripe-projects/SKILL.md +139 -0
  98. package/.goose/skills/upgrade-stripe/SKILL.md +185 -0
  99. package/.iflow/skills/stripe-best-practices/SKILL.md +42 -0
  100. package/.iflow/skills/stripe-best-practices/references/billing.md +36 -0
  101. package/.iflow/skills/stripe-best-practices/references/connect.md +48 -0
  102. package/.iflow/skills/stripe-best-practices/references/payments.md +79 -0
  103. package/.iflow/skills/stripe-best-practices/references/security.md +109 -0
  104. package/.iflow/skills/stripe-best-practices/references/treasury.md +16 -0
  105. package/.iflow/skills/stripe-projects/SKILL.md +139 -0
  106. package/.iflow/skills/upgrade-stripe/SKILL.md +185 -0
  107. package/.junie/skills/stripe-best-practices/SKILL.md +42 -0
  108. package/.junie/skills/stripe-best-practices/references/billing.md +36 -0
  109. package/.junie/skills/stripe-best-practices/references/connect.md +48 -0
  110. package/.junie/skills/stripe-best-practices/references/payments.md +79 -0
  111. package/.junie/skills/stripe-best-practices/references/security.md +109 -0
  112. package/.junie/skills/stripe-best-practices/references/treasury.md +16 -0
  113. package/.junie/skills/stripe-projects/SKILL.md +139 -0
  114. package/.junie/skills/upgrade-stripe/SKILL.md +185 -0
  115. package/.kilocode/skills/stripe-best-practices/SKILL.md +42 -0
  116. package/.kilocode/skills/stripe-best-practices/references/billing.md +36 -0
  117. package/.kilocode/skills/stripe-best-practices/references/connect.md +48 -0
  118. package/.kilocode/skills/stripe-best-practices/references/payments.md +79 -0
  119. package/.kilocode/skills/stripe-best-practices/references/security.md +109 -0
  120. package/.kilocode/skills/stripe-best-practices/references/treasury.md +16 -0
  121. package/.kilocode/skills/stripe-projects/SKILL.md +139 -0
  122. package/.kilocode/skills/upgrade-stripe/SKILL.md +185 -0
  123. package/.kiro/skills/stripe-best-practices/SKILL.md +42 -0
  124. package/.kiro/skills/stripe-best-practices/references/billing.md +36 -0
  125. package/.kiro/skills/stripe-best-practices/references/connect.md +48 -0
  126. package/.kiro/skills/stripe-best-practices/references/payments.md +79 -0
  127. package/.kiro/skills/stripe-best-practices/references/security.md +109 -0
  128. package/.kiro/skills/stripe-best-practices/references/treasury.md +16 -0
  129. package/.kiro/skills/stripe-projects/SKILL.md +139 -0
  130. package/.kiro/skills/upgrade-stripe/SKILL.md +185 -0
  131. package/.kode/skills/stripe-best-practices/SKILL.md +42 -0
  132. package/.kode/skills/stripe-best-practices/references/billing.md +36 -0
  133. package/.kode/skills/stripe-best-practices/references/connect.md +48 -0
  134. package/.kode/skills/stripe-best-practices/references/payments.md +79 -0
  135. package/.kode/skills/stripe-best-practices/references/security.md +109 -0
  136. package/.kode/skills/stripe-best-practices/references/treasury.md +16 -0
  137. package/.kode/skills/stripe-projects/SKILL.md +139 -0
  138. package/.kode/skills/upgrade-stripe/SKILL.md +185 -0
  139. package/.mcpjam/skills/stripe-best-practices/SKILL.md +42 -0
  140. package/.mcpjam/skills/stripe-best-practices/references/billing.md +36 -0
  141. package/.mcpjam/skills/stripe-best-practices/references/connect.md +48 -0
  142. package/.mcpjam/skills/stripe-best-practices/references/payments.md +79 -0
  143. package/.mcpjam/skills/stripe-best-practices/references/security.md +109 -0
  144. package/.mcpjam/skills/stripe-best-practices/references/treasury.md +16 -0
  145. package/.mcpjam/skills/stripe-projects/SKILL.md +139 -0
  146. package/.mcpjam/skills/upgrade-stripe/SKILL.md +185 -0
  147. package/.mux/skills/stripe-best-practices/SKILL.md +42 -0
  148. package/.mux/skills/stripe-best-practices/references/billing.md +36 -0
  149. package/.mux/skills/stripe-best-practices/references/connect.md +48 -0
  150. package/.mux/skills/stripe-best-practices/references/payments.md +79 -0
  151. package/.mux/skills/stripe-best-practices/references/security.md +109 -0
  152. package/.mux/skills/stripe-best-practices/references/treasury.md +16 -0
  153. package/.mux/skills/stripe-projects/SKILL.md +139 -0
  154. package/.mux/skills/upgrade-stripe/SKILL.md +185 -0
  155. package/.neovate/skills/stripe-best-practices/SKILL.md +42 -0
  156. package/.neovate/skills/stripe-best-practices/references/billing.md +36 -0
  157. package/.neovate/skills/stripe-best-practices/references/connect.md +48 -0
  158. package/.neovate/skills/stripe-best-practices/references/payments.md +79 -0
  159. package/.neovate/skills/stripe-best-practices/references/security.md +109 -0
  160. package/.neovate/skills/stripe-best-practices/references/treasury.md +16 -0
  161. package/.neovate/skills/stripe-projects/SKILL.md +139 -0
  162. package/.neovate/skills/upgrade-stripe/SKILL.md +185 -0
  163. package/.nixpacksignore +14 -0
  164. package/.openhands/skills/stripe-best-practices/SKILL.md +42 -0
  165. package/.openhands/skills/stripe-best-practices/references/billing.md +36 -0
  166. package/.openhands/skills/stripe-best-practices/references/connect.md +48 -0
  167. package/.openhands/skills/stripe-best-practices/references/payments.md +79 -0
  168. package/.openhands/skills/stripe-best-practices/references/security.md +109 -0
  169. package/.openhands/skills/stripe-best-practices/references/treasury.md +16 -0
  170. package/.openhands/skills/stripe-projects/SKILL.md +139 -0
  171. package/.openhands/skills/upgrade-stripe/SKILL.md +185 -0
  172. package/.pi/skills/stripe-best-practices/SKILL.md +42 -0
  173. package/.pi/skills/stripe-best-practices/references/billing.md +36 -0
  174. package/.pi/skills/stripe-best-practices/references/connect.md +48 -0
  175. package/.pi/skills/stripe-best-practices/references/payments.md +79 -0
  176. package/.pi/skills/stripe-best-practices/references/security.md +109 -0
  177. package/.pi/skills/stripe-best-practices/references/treasury.md +16 -0
  178. package/.pi/skills/stripe-projects/SKILL.md +139 -0
  179. package/.pi/skills/upgrade-stripe/SKILL.md +185 -0
  180. package/.pochi/skills/stripe-best-practices/SKILL.md +42 -0
  181. package/.pochi/skills/stripe-best-practices/references/billing.md +36 -0
  182. package/.pochi/skills/stripe-best-practices/references/connect.md +48 -0
  183. package/.pochi/skills/stripe-best-practices/references/payments.md +79 -0
  184. package/.pochi/skills/stripe-best-practices/references/security.md +109 -0
  185. package/.pochi/skills/stripe-best-practices/references/treasury.md +16 -0
  186. package/.pochi/skills/stripe-projects/SKILL.md +139 -0
  187. package/.pochi/skills/upgrade-stripe/SKILL.md +185 -0
  188. package/.qoder/skills/stripe-best-practices/SKILL.md +42 -0
  189. package/.qoder/skills/stripe-best-practices/references/billing.md +36 -0
  190. package/.qoder/skills/stripe-best-practices/references/connect.md +48 -0
  191. package/.qoder/skills/stripe-best-practices/references/payments.md +79 -0
  192. package/.qoder/skills/stripe-best-practices/references/security.md +109 -0
  193. package/.qoder/skills/stripe-best-practices/references/treasury.md +16 -0
  194. package/.qoder/skills/stripe-projects/SKILL.md +139 -0
  195. package/.qoder/skills/upgrade-stripe/SKILL.md +185 -0
  196. package/.qwen/skills/stripe-best-practices/SKILL.md +42 -0
  197. package/.qwen/skills/stripe-best-practices/references/billing.md +36 -0
  198. package/.qwen/skills/stripe-best-practices/references/connect.md +48 -0
  199. package/.qwen/skills/stripe-best-practices/references/payments.md +79 -0
  200. package/.qwen/skills/stripe-best-practices/references/security.md +109 -0
  201. package/.qwen/skills/stripe-best-practices/references/treasury.md +16 -0
  202. package/.qwen/skills/stripe-projects/SKILL.md +139 -0
  203. package/.qwen/skills/upgrade-stripe/SKILL.md +185 -0
  204. package/.roo/skills/stripe-best-practices/SKILL.md +42 -0
  205. package/.roo/skills/stripe-best-practices/references/billing.md +36 -0
  206. package/.roo/skills/stripe-best-practices/references/connect.md +48 -0
  207. package/.roo/skills/stripe-best-practices/references/payments.md +79 -0
  208. package/.roo/skills/stripe-best-practices/references/security.md +109 -0
  209. package/.roo/skills/stripe-best-practices/references/treasury.md +16 -0
  210. package/.roo/skills/stripe-projects/SKILL.md +139 -0
  211. package/.roo/skills/upgrade-stripe/SKILL.md +185 -0
  212. package/.trae/skills/stripe-best-practices/SKILL.md +42 -0
  213. package/.trae/skills/stripe-best-practices/references/billing.md +36 -0
  214. package/.trae/skills/stripe-best-practices/references/connect.md +48 -0
  215. package/.trae/skills/stripe-best-practices/references/payments.md +79 -0
  216. package/.trae/skills/stripe-best-practices/references/security.md +109 -0
  217. package/.trae/skills/stripe-best-practices/references/treasury.md +16 -0
  218. package/.trae/skills/stripe-projects/SKILL.md +139 -0
  219. package/.trae/skills/upgrade-stripe/SKILL.md +185 -0
  220. package/.vibe/skills/stripe-best-practices/SKILL.md +42 -0
  221. package/.vibe/skills/stripe-best-practices/references/billing.md +36 -0
  222. package/.vibe/skills/stripe-best-practices/references/connect.md +48 -0
  223. package/.vibe/skills/stripe-best-practices/references/payments.md +79 -0
  224. package/.vibe/skills/stripe-best-practices/references/security.md +109 -0
  225. package/.vibe/skills/stripe-best-practices/references/treasury.md +16 -0
  226. package/.vibe/skills/stripe-projects/SKILL.md +139 -0
  227. package/.vibe/skills/upgrade-stripe/SKILL.md +185 -0
  228. package/.windsurf/skills/stripe-best-practices/SKILL.md +42 -0
  229. package/.windsurf/skills/stripe-best-practices/references/billing.md +36 -0
  230. package/.windsurf/skills/stripe-best-practices/references/connect.md +48 -0
  231. package/.windsurf/skills/stripe-best-practices/references/payments.md +79 -0
  232. package/.windsurf/skills/stripe-best-practices/references/security.md +109 -0
  233. package/.windsurf/skills/stripe-best-practices/references/treasury.md +16 -0
  234. package/.windsurf/skills/stripe-projects/SKILL.md +139 -0
  235. package/.windsurf/skills/upgrade-stripe/SKILL.md +185 -0
  236. package/.zencoder/skills/stripe-best-practices/SKILL.md +42 -0
  237. package/.zencoder/skills/stripe-best-practices/references/billing.md +36 -0
  238. package/.zencoder/skills/stripe-best-practices/references/connect.md +48 -0
  239. package/.zencoder/skills/stripe-best-practices/references/payments.md +79 -0
  240. package/.zencoder/skills/stripe-best-practices/references/security.md +109 -0
  241. package/.zencoder/skills/stripe-best-practices/references/treasury.md +16 -0
  242. package/.zencoder/skills/stripe-projects/SKILL.md +139 -0
  243. package/.zencoder/skills/upgrade-stripe/SKILL.md +185 -0
  244. package/AUDIT.md +95 -0
  245. package/BLOCKERS.md +0 -0
  246. package/COOLIFY.md +51 -0
  247. package/MCP_SETUP.md +23 -0
  248. package/PRODUCTION_CHECKLIST.md +246 -0
  249. package/README.md +47 -0
  250. package/ROADMAP.md +91 -0
  251. package/docs/superpowers/plans/2026-05-11-availsync-frontend-sales-flow.md +2445 -0
  252. package/frontend/.env.example +2 -0
  253. package/frontend/app/admin/layout.tsx +13 -0
  254. package/frontend/app/admin/page.tsx +747 -0
  255. package/frontend/app/app/activity/page.tsx +257 -0
  256. package/frontend/app/app/agents/[agentId]/page.tsx +21 -0
  257. package/frontend/app/app/agents/page.tsx +1155 -0
  258. package/frontend/app/app/audit/page.tsx +225 -0
  259. package/frontend/app/app/availability/page.tsx +840 -0
  260. package/frontend/app/app/holds/page.tsx +262 -0
  261. package/frontend/app/app/layout.tsx +19 -0
  262. package/frontend/app/app/onboarding/page.tsx +10 -0
  263. package/frontend/app/app/onboarding/verify/page.tsx +309 -0
  264. package/frontend/app/app/page.tsx +508 -0
  265. package/frontend/app/app/settings/page.tsx +399 -0
  266. package/frontend/app/app/work/page.tsx +426 -0
  267. package/frontend/app/changelog/page.tsx +93 -0
  268. package/frontend/app/checkout/page.tsx +25 -0
  269. package/frontend/app/docs/api/page.tsx +157 -0
  270. package/frontend/app/docs/page.tsx +296 -0
  271. package/frontend/app/docs/pilot/page.tsx +127 -0
  272. package/frontend/app/docs/quickstart/page.tsx +318 -0
  273. package/frontend/app/docs/reliability/page.tsx +78 -0
  274. package/frontend/app/docs/sdk/node/page.tsx +166 -0
  275. package/frontend/app/globals.css +57 -0
  276. package/frontend/app/icon.png +0 -0
  277. package/frontend/app/layout.tsx +87 -0
  278. package/frontend/app/login/page.tsx +14 -0
  279. package/frontend/app/page.tsx +47 -0
  280. package/frontend/app/pricing/page.tsx +66 -0
  281. package/frontend/app/privacy/page.tsx +52 -0
  282. package/frontend/app/robots.ts +26 -0
  283. package/frontend/app/security/page.tsx +74 -0
  284. package/frontend/app/signup/page.tsx +14 -0
  285. package/frontend/app/sitemap.ts +14 -0
  286. package/frontend/app/terms/page.tsx +51 -0
  287. package/frontend/components/brand/AvailsyncLogo.tsx +56 -0
  288. package/frontend/components/checkout/CheckoutClient.tsx +100 -0
  289. package/frontend/components/dashboard/AgentForm.tsx +59 -0
  290. package/frontend/components/dashboard/AppShell.tsx +291 -0
  291. package/frontend/components/dashboard/AvailabilityChecker.tsx +117 -0
  292. package/frontend/components/dashboard/AvailabilityWindowForm.tsx +40 -0
  293. package/frontend/components/dashboard/HoldForm.tsx +133 -0
  294. package/frontend/components/dashboard/MetricCard.tsx +10 -0
  295. package/frontend/components/login/LoginForm.tsx +95 -0
  296. package/frontend/components/marketing/AgentCoordinationStory.tsx +1530 -0
  297. package/frontend/components/marketing/Faq.tsx +41 -0
  298. package/frontend/components/marketing/Hero.tsx +73 -0
  299. package/frontend/components/marketing/HowItWorks.tsx +28 -0
  300. package/frontend/components/marketing/ObserveModeTeaser.tsx +41 -0
  301. package/frontend/components/marketing/PricingTeaser.tsx +23 -0
  302. package/frontend/components/marketing/ProblemSolution.tsx +36 -0
  303. package/frontend/components/marketing/SiteFooter.tsx +59 -0
  304. package/frontend/components/marketing/SiteHeader.tsx +45 -0
  305. package/frontend/components/marketing/UseCases.tsx +27 -0
  306. package/frontend/components/onboarding/OnboardingClient.tsx +278 -0
  307. package/frontend/components/pricing/PricingCards.tsx +65 -0
  308. package/frontend/components/privacy/CookieConsent.tsx +230 -0
  309. package/frontend/components/privacy/CookieSettingsButton.tsx +15 -0
  310. package/frontend/components/seo/JsonLd.tsx +10 -0
  311. package/frontend/components/signup/SignupForm.tsx +55 -0
  312. package/frontend/components/ui/Badge.tsx +23 -0
  313. package/frontend/components/ui/Button.tsx +37 -0
  314. package/frontend/components/ui/Card.tsx +11 -0
  315. package/frontend/components/ui/ConfirmDialog.tsx +77 -0
  316. package/frontend/components/ui/EmptyState.tsx +24 -0
  317. package/frontend/components/ui/Input.tsx +14 -0
  318. package/frontend/components/ui/KeyDisplay.tsx +49 -0
  319. package/frontend/components/ui/Select.tsx +14 -0
  320. package/frontend/components/ui/Skeleton.tsx +24 -0
  321. package/frontend/components/ui/Tabs.tsx +19 -0
  322. package/frontend/components/ui/Textarea.tsx +14 -0
  323. package/frontend/components/ui/Toast.tsx +78 -0
  324. package/frontend/components/waitlist/WaitlistDialog.tsx +128 -0
  325. package/frontend/lib/api.ts +1282 -0
  326. package/frontend/lib/billing.ts +6 -0
  327. package/frontend/lib/cookieConsent.ts +113 -0
  328. package/frontend/lib/format.ts +16 -0
  329. package/frontend/lib/plans.ts +62 -0
  330. package/frontend/lib/schemas.ts +108 -0
  331. package/frontend/lib/seo.ts +376 -0
  332. package/frontend/lib/setupGuides.ts +630 -0
  333. package/frontend/lib/storage.ts +30 -0
  334. package/frontend/next-env.d.ts +6 -0
  335. package/frontend/next.config.mjs +13 -0
  336. package/frontend/package-lock.json +14409 -0
  337. package/frontend/package.json +41 -0
  338. package/frontend/playwright.config.ts +20 -0
  339. package/frontend/postcss.config.mjs +8 -0
  340. package/frontend/public/.gitkeep +0 -0
  341. package/frontend/public/brand/availsync-logo-board.png +0 -0
  342. package/frontend/public/brand/availsync-logo-dark.png +0 -0
  343. package/frontend/public/brand/availsync-mark-dark.png +0 -0
  344. package/frontend/public/brand/availsync-wordmark-dark.png +0 -0
  345. package/frontend/public/marketing/hero-agent-coordination.png +0 -0
  346. package/frontend/tailwind.config.ts +53 -0
  347. package/frontend/tests/smoke.spec.ts +89 -0
  348. package/frontend/tsconfig.json +23 -0
  349. package/jest.config.js +7 -0
  350. package/nixpacks.toml +11 -0
  351. package/package.json +53 -0
  352. package/packages/mcp/LICENSE +21 -0
  353. package/packages/mcp/README.md +60 -0
  354. package/packages/mcp/jest.config.cjs +8 -0
  355. package/packages/mcp/package.json +54 -0
  356. package/packages/mcp/src/helpers.ts +38 -0
  357. package/packages/mcp/src/index.test.ts +60 -0
  358. package/packages/mcp/src/index.ts +387 -0
  359. package/packages/mcp/tsconfig.json +20 -0
  360. package/packages/mcp/tsconfig.test.json +12 -0
  361. package/packages/node/LICENSE +21 -0
  362. package/packages/node/README.md +120 -0
  363. package/packages/node/jest.config.cjs +8 -0
  364. package/packages/node/package.json +46 -0
  365. package/packages/node/src/index.test.ts +360 -0
  366. package/packages/node/src/index.ts +402 -0
  367. package/packages/node/tsconfig.json +20 -0
  368. package/packages/node/tsconfig.test.json +12 -0
  369. package/plan.md +923 -0
  370. package/skills/stripe-best-practices/SKILL.md +42 -0
  371. package/skills/stripe-best-practices/references/billing.md +36 -0
  372. package/skills/stripe-best-practices/references/connect.md +48 -0
  373. package/skills/stripe-best-practices/references/payments.md +79 -0
  374. package/skills/stripe-best-practices/references/security.md +109 -0
  375. package/skills/stripe-best-practices/references/treasury.md +16 -0
  376. package/skills/stripe-projects/SKILL.md +139 -0
  377. package/skills/upgrade-stripe/SKILL.md +185 -0
  378. package/skills-lock.json +20 -0
  379. package/src/core/availability.ts +178 -0
  380. package/src/core/conflict.ts +209 -0
  381. package/src/core/work.ts +490 -0
  382. package/src/db/client.ts +17 -0
  383. package/src/db/migrations/001_init.sql +88 -0
  384. package/src/db/migrations/002_stripe.sql +2 -0
  385. package/src/db/migrations/003_workspace_auth.sql +19 -0
  386. package/src/db/migrations/004_agent_mcp_status.sql +2 -0
  387. package/src/db/migrations/005_hold_event_actor.sql +4 -0
  388. package/src/db/migrations/006_agent_activity.sql +35 -0
  389. package/src/db/migrations/007_work_coordination.sql +60 -0
  390. package/src/db/migrations/008_work_claim_leases.sql +20 -0
  391. package/src/db/migrations/009_billing_subscription_state.sql +23 -0
  392. package/src/db/migrations/010_agent_api_key_prefix.sql +10 -0
  393. package/src/db/migrations/011_org_verified_and_work_event_retention.sql +11 -0
  394. package/src/db/migrations/012_agent_enforcement_mode.sql +12 -0
  395. package/src/db/migrations/013_support_tickets.sql +21 -0
  396. package/src/db/migrations/014_paid_plan_waitlist.sql +23 -0
  397. package/src/db/migrations/015_agent_last_seen.sql +2 -0
  398. package/src/db/migrations.ts +164 -0
  399. package/src/db/run-migrations.ts +13 -0
  400. package/src/index.ts +183 -0
  401. package/src/lib/activity.ts +137 -0
  402. package/src/lib/apiKeys.ts +32 -0
  403. package/src/lib/appInfo.ts +26 -0
  404. package/src/lib/billingConfig.ts +3 -0
  405. package/src/lib/env.ts +75 -0
  406. package/src/lib/logger.ts +8 -0
  407. package/src/lib/plans.ts +204 -0
  408. package/src/mcp/server.js +5 -0
  409. package/src/mcp/server.ts +350 -0
  410. package/src/middleware/auth.ts +342 -0
  411. package/src/middleware/requestId.ts +16 -0
  412. package/src/routes/account.ts +168 -0
  413. package/src/routes/activity.ts +126 -0
  414. package/src/routes/admin.ts +514 -0
  415. package/src/routes/audit.ts +68 -0
  416. package/src/routes/auth.ts +203 -0
  417. package/src/routes/availability.ts +325 -0
  418. package/src/routes/billing.ts +406 -0
  419. package/src/routes/conflicts.ts +131 -0
  420. package/src/routes/holds.ts +437 -0
  421. package/src/routes/mcp.ts +57 -0
  422. package/src/routes/metrics.ts +39 -0
  423. package/src/routes/onboarding.ts +273 -0
  424. package/src/routes/orgs.ts +981 -0
  425. package/src/routes/preferences.ts +132 -0
  426. package/src/routes/session.ts +16 -0
  427. package/src/routes/support.ts +77 -0
  428. package/src/routes/value.ts +186 -0
  429. package/src/routes/waitlist.ts +63 -0
  430. package/src/routes/work.ts +1578 -0
  431. package/src/server.ts +36 -0
  432. package/src/types/index.ts +109 -0
  433. package/tests/integration/activity.route.test.ts +103 -0
  434. package/tests/integration/admin.route.test.ts +143 -0
  435. package/tests/integration/agent-keys.route.test.ts +237 -0
  436. package/tests/integration/availability.route.test.ts +125 -0
  437. package/tests/integration/billing.route.test.ts +393 -0
  438. package/tests/integration/conflicts.route.test.ts +131 -0
  439. package/tests/integration/flows.test.ts +154 -0
  440. package/tests/integration/helpers.ts +134 -0
  441. package/tests/integration/holds.route.test.ts +185 -0
  442. package/tests/integration/metrics.route.test.ts +100 -0
  443. package/tests/integration/onboarding.verify.route.test.ts +163 -0
  444. package/tests/integration/preferences.route.test.ts +53 -0
  445. package/tests/integration/session.route.test.ts +97 -0
  446. package/tests/integration/system.route.test.ts +92 -0
  447. package/tests/integration/value.route.test.ts +235 -0
  448. package/tests/integration/work.route.test.ts +745 -0
  449. package/tests/setup.ts +4 -0
  450. package/tests/smoke.sh +62 -0
  451. package/tests/unit/auth.test.ts +114 -0
  452. package/tests/unit/availability.test.ts +149 -0
  453. package/tests/unit/conflict.test.ts +118 -0
  454. package/tests/unit/env.test.ts +69 -0
  455. package/tests/unit/migrations.test.ts +135 -0
  456. package/tests/unit/request-id.test.ts +37 -0
  457. package/tmp-mobile-agents.png +0 -0
  458. package/tmp-next-mobile.err.log +10 -0
  459. package/tmp-next-mobile.log +5 -0
  460. package/tsconfig.json +16 -0
@@ -0,0 +1,65 @@
1
+ 'use client';
2
+
3
+ import { Check } from 'lucide-react';
4
+ import { useState } from 'react';
5
+ import { Button } from '@/components/ui/Button';
6
+ import { Card } from '@/components/ui/Card';
7
+ import { billingUpgradesEnabled } from '@/lib/billing';
8
+ import { plans } from '@/lib/plans';
9
+ import { WaitlistDialog } from '@/components/waitlist/WaitlistDialog';
10
+
11
+ export function PricingCards() {
12
+ const upgradesEnabled = billingUpgradesEnabled();
13
+ const [waitlistPlan, setWaitlistPlan] = useState<'individual' | 'team' | null>(null);
14
+ return (
15
+ <>
16
+ <div id="waitlist" className="grid gap-4 md:grid-cols-3">
17
+ {plans.map((plan) => {
18
+ const paidWaitlist = plan.id !== 'free' && !upgradesEnabled;
19
+ return (
20
+ <Card className={plan.id === 'individual' ? 'border-accent bg-surface-raised' : ''} key={plan.id}>
21
+ <div className="flex items-center justify-between gap-3">
22
+ <p className="text-label font-semibold uppercase text-text-tertiary">{plan.name}</p>
23
+ {paidWaitlist && (
24
+ <span className="rounded border border-border bg-bg px-2 py-1 text-label text-text-tertiary">
25
+ Waitlist
26
+ </span>
27
+ )}
28
+ </div>
29
+ <p className="mt-4 text-4xl font-semibold text-text-primary">
30
+ {paidWaitlist ? 'Waitlist' : plan.price}
31
+ {!paidWaitlist && <span className="text-base text-text-tertiary">{plan.period}</span>}
32
+ </p>
33
+ <p className="mt-3 min-h-14 text-body leading-6 text-text-secondary">
34
+ {paidWaitlist ? `${plan.description} Paid upgrades are in private pilot.` : plan.description}
35
+ </p>
36
+ <Button
37
+ className="mt-6 w-full"
38
+ href={paidWaitlist ? undefined : plan.href}
39
+ onClick={paidWaitlist ? () => setWaitlistPlan(plan.id as 'individual' | 'team') : undefined}
40
+ >
41
+ {paidWaitlist ? 'Join waitlist' : plan.cta}
42
+ </Button>
43
+ <div className="mt-6 space-y-3">
44
+ {plan.features.map((feature) => (
45
+ <p className="flex gap-2 text-body text-text-secondary" key={feature}>
46
+ <Check className="h-4 w-4 text-success" />
47
+ {feature}
48
+ </p>
49
+ ))}
50
+ </div>
51
+ </Card>
52
+ );
53
+ })}
54
+ </div>
55
+ {waitlistPlan && (
56
+ <WaitlistDialog
57
+ open
58
+ plan={waitlistPlan}
59
+ source="pricing"
60
+ onClose={() => setWaitlistPlan(null)}
61
+ />
62
+ )}
63
+ </>
64
+ );
65
+ }
@@ -0,0 +1,230 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { X } from 'lucide-react';
5
+ import {
6
+ getCookieConsent,
7
+ OPEN_COOKIE_SETTINGS_EVENT,
8
+ setCookieConsent,
9
+ type CookieConsent as StoredCookieConsent,
10
+ } from '@/lib/cookieConsent';
11
+
12
+ type PreferenceState = {
13
+ analytics: boolean;
14
+ marketing: boolean;
15
+ };
16
+
17
+ const defaultPreferences: PreferenceState = {
18
+ analytics: false,
19
+ marketing: false,
20
+ };
21
+
22
+ function preferencesFromConsent(consent: StoredCookieConsent | null): PreferenceState {
23
+ return {
24
+ analytics: consent?.categories.analytics ?? false,
25
+ marketing: consent?.categories.marketing ?? false,
26
+ };
27
+ }
28
+
29
+ export function CookieConsent() {
30
+ const [loaded, setLoaded] = useState(false);
31
+ const [showBanner, setShowBanner] = useState(false);
32
+ const [showModal, setShowModal] = useState(false);
33
+ const [preferences, setPreferences] = useState<PreferenceState>(defaultPreferences);
34
+ const modalRef = useRef<HTMLDivElement>(null);
35
+
36
+ useEffect(() => {
37
+ const existing = getCookieConsent();
38
+ setPreferences(preferencesFromConsent(existing));
39
+ setShowBanner(!existing);
40
+ setLoaded(true);
41
+
42
+ const openSettings = () => {
43
+ const latest = getCookieConsent();
44
+ setPreferences(preferencesFromConsent(latest));
45
+ setShowBanner(false);
46
+ setShowModal(true);
47
+ };
48
+
49
+ window.addEventListener(OPEN_COOKIE_SETTINGS_EVENT, openSettings);
50
+ return () => window.removeEventListener(OPEN_COOKIE_SETTINGS_EVENT, openSettings);
51
+ }, []);
52
+
53
+ useEffect(() => {
54
+ if (!showModal) return;
55
+
56
+ modalRef.current?.focus();
57
+ const onKeyDown = (event: KeyboardEvent) => {
58
+ if (event.key === 'Escape') setShowModal(false);
59
+ };
60
+
61
+ window.addEventListener('keydown', onKeyDown);
62
+ return () => window.removeEventListener('keydown', onKeyDown);
63
+ }, [showModal]);
64
+
65
+ function save(nextPreferences: PreferenceState) {
66
+ setCookieConsent(nextPreferences);
67
+ setPreferences(nextPreferences);
68
+ setShowBanner(false);
69
+ setShowModal(false);
70
+ }
71
+
72
+ if (!loaded) return null;
73
+
74
+ return (
75
+ <>
76
+ {showBanner && (
77
+ <section
78
+ aria-label="Cookie notice"
79
+ className="fixed inset-x-0 bottom-0 z-[80] border-t border-border bg-bg/96 px-4 py-4 text-text-primary shadow-[0_-18px_60px_rgba(0,0,0,0.45)] backdrop-blur"
80
+ >
81
+ <div className="mx-auto flex max-w-6xl flex-col gap-4 md:flex-row md:items-center md:justify-between">
82
+ <div className="max-w-2xl">
83
+ <p className="text-heading font-semibold">Cookies on Availsync</p>
84
+ <p className="mt-1 text-body leading-6 text-text-secondary">
85
+ We use necessary cookies for login and security. Optional analytics and marketing
86
+ cookies stay off unless you allow them.
87
+ </p>
88
+ </div>
89
+ <div className="grid gap-2 sm:grid-cols-3 md:flex md:shrink-0">
90
+ <button
91
+ className="min-h-11 rounded border border-border px-4 text-body font-medium text-text-secondary transition hover:border-border-focus hover:text-text-primary"
92
+ onClick={() => setShowModal(true)}
93
+ >
94
+ Manage preferences
95
+ </button>
96
+ <button
97
+ className="min-h-11 rounded border border-border px-4 text-body font-medium text-text-secondary transition hover:border-border-focus hover:text-text-primary"
98
+ onClick={() => save({ analytics: false, marketing: false })}
99
+ >
100
+ Reject optional
101
+ </button>
102
+ <button
103
+ className="min-h-11 rounded bg-accent px-4 text-body font-medium text-white transition hover:bg-accent-hover"
104
+ onClick={() => save({ analytics: true, marketing: true })}
105
+ >
106
+ Accept all
107
+ </button>
108
+ </div>
109
+ </div>
110
+ </section>
111
+ )}
112
+
113
+ {showModal && (
114
+ <div className="fixed inset-0 z-[90] flex items-end justify-center bg-black/60 p-3 sm:items-center sm:p-6">
115
+ <div
116
+ aria-labelledby="cookie-preferences-title"
117
+ aria-modal="true"
118
+ className="max-h-[calc(100dvh-24px)] w-full max-w-xl overflow-y-auto rounded border border-border bg-surface p-5 text-text-primary shadow-2xl outline-none"
119
+ ref={modalRef}
120
+ role="dialog"
121
+ tabIndex={-1}
122
+ >
123
+ <div className="flex items-start justify-between gap-4">
124
+ <div>
125
+ <p className="text-label uppercase text-text-tertiary">Privacy controls</p>
126
+ <h2 className="mt-1 text-title font-semibold" id="cookie-preferences-title">
127
+ Cookie preferences
128
+ </h2>
129
+ </div>
130
+ <button
131
+ aria-label="Close cookie preferences"
132
+ className="flex min-h-10 min-w-10 items-center justify-center rounded border border-border text-text-secondary transition hover:border-border-focus hover:text-text-primary"
133
+ onClick={() => setShowModal(false)}
134
+ >
135
+ <X aria-hidden="true" className="h-4 w-4" />
136
+ </button>
137
+ </div>
138
+
139
+ <p className="mt-4 text-body leading-6 text-text-secondary">
140
+ Necessary cookies keep login and security working. Optional categories are prepared
141
+ for future analytics and marketing, but no third-party tracking is loaded in this
142
+ slice.
143
+ </p>
144
+
145
+ <div className="mt-5 space-y-3">
146
+ <div className="rounded border border-border bg-bg p-4">
147
+ <div className="flex items-start justify-between gap-4">
148
+ <div>
149
+ <h3 className="text-heading font-semibold">Necessary</h3>
150
+ <p className="mt-1 text-body leading-6 text-text-secondary">
151
+ Required for dashboard sessions, security, and saving this preference.
152
+ </p>
153
+ </div>
154
+ <span className="rounded bg-success/15 px-2 py-1 text-label font-medium uppercase text-success">
155
+ Always on
156
+ </span>
157
+ </div>
158
+ </div>
159
+
160
+ <label className="block rounded border border-border bg-bg p-4">
161
+ <span className="flex items-start justify-between gap-4">
162
+ <span>
163
+ <span className="block text-heading font-semibold">Analytics</span>
164
+ <span className="mt-1 block text-body leading-6 text-text-secondary">
165
+ Helps measure product usage if analytics is added later.
166
+ </span>
167
+ </span>
168
+ <input
169
+ checked={preferences.analytics}
170
+ className="mt-1 h-5 w-5 accent-accent"
171
+ onChange={(event) =>
172
+ setPreferences((current) => ({
173
+ ...current,
174
+ analytics: event.target.checked,
175
+ }))
176
+ }
177
+ type="checkbox"
178
+ />
179
+ </span>
180
+ </label>
181
+
182
+ <label className="block rounded border border-border bg-bg p-4">
183
+ <span className="flex items-start justify-between gap-4">
184
+ <span>
185
+ <span className="block text-heading font-semibold">Marketing</span>
186
+ <span className="mt-1 block text-body leading-6 text-text-secondary">
187
+ Allows campaign attribution or ad pixels if they are added later.
188
+ </span>
189
+ </span>
190
+ <input
191
+ checked={preferences.marketing}
192
+ className="mt-1 h-5 w-5 accent-accent"
193
+ onChange={(event) =>
194
+ setPreferences((current) => ({
195
+ ...current,
196
+ marketing: event.target.checked,
197
+ }))
198
+ }
199
+ type="checkbox"
200
+ />
201
+ </span>
202
+ </label>
203
+ </div>
204
+
205
+ <div className="mt-5 grid gap-2 sm:grid-cols-3">
206
+ <button
207
+ className="min-h-11 rounded border border-border px-4 text-body font-medium text-text-secondary transition hover:border-border-focus hover:text-text-primary"
208
+ onClick={() => save({ analytics: false, marketing: false })}
209
+ >
210
+ Reject optional
211
+ </button>
212
+ <button
213
+ className="min-h-11 rounded border border-border px-4 text-body font-medium text-text-secondary transition hover:border-border-focus hover:text-text-primary"
214
+ onClick={() => save(preferences)}
215
+ >
216
+ Save choices
217
+ </button>
218
+ <button
219
+ className="min-h-11 rounded bg-accent px-4 text-body font-medium text-white transition hover:bg-accent-hover"
220
+ onClick={() => save({ analytics: true, marketing: true })}
221
+ >
222
+ Accept all
223
+ </button>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ )}
228
+ </>
229
+ );
230
+ }
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+
3
+ import { OPEN_COOKIE_SETTINGS_EVENT } from '@/lib/cookieConsent';
4
+
5
+ export function CookieSettingsButton({ className }: { className?: string }) {
6
+ return (
7
+ <button
8
+ className={className}
9
+ onClick={() => window.dispatchEvent(new Event(OPEN_COOKIE_SETTINGS_EVENT))}
10
+ type="button"
11
+ >
12
+ Cookie settings
13
+ </button>
14
+ );
15
+ }
@@ -0,0 +1,10 @@
1
+ export function JsonLd({ data }: { data: Record<string, unknown> }) {
2
+ return (
3
+ <script
4
+ type="application/ld+json"
5
+ dangerouslySetInnerHTML={{
6
+ __html: JSON.stringify(data).replace(/</g, '\\u003c'),
7
+ }}
8
+ />
9
+ );
10
+ }
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { useRouter, useSearchParams } from 'next/navigation';
5
+ import { createOrg } from '@/lib/api';
6
+ import { Button } from '@/components/ui/Button';
7
+ import { Input } from '@/components/ui/Input';
8
+
9
+ export function SignupForm() {
10
+ const router = useRouter();
11
+ const searchParams = useSearchParams();
12
+ const [name, setName] = useState('');
13
+ const [error, setError] = useState('');
14
+ const [loading, setLoading] = useState(false);
15
+
16
+ async function onSubmit(event: React.FormEvent) {
17
+ event.preventDefault();
18
+ setError('');
19
+ setLoading(true);
20
+ try {
21
+ const org = await createOrg(name);
22
+ router.push(`/app/onboarding?orgId=${org.id}&plan=${searchParams.get('plan') || 'free'}`);
23
+ } catch (err) {
24
+ setError(err instanceof Error ? err.message : 'Could not create organization');
25
+ } finally {
26
+ setLoading(false);
27
+ }
28
+ }
29
+
30
+ return (
31
+ <div className="mt-8 grid gap-4">
32
+ <form className="rounded border border-border bg-surface p-5" onSubmit={onSubmit}>
33
+ <label className="text-label uppercase text-text-tertiary" htmlFor="org-name">
34
+ Organization name
35
+ </label>
36
+ <Input
37
+ id="org-name"
38
+ required
39
+ value={name}
40
+ onChange={(event) => setName(event.target.value)}
41
+ />
42
+ {error && <p className="mt-3 text-body text-error">{error}</p>}
43
+ <Button className="mt-5 w-full" disabled={loading} type="submit">
44
+ {loading ? 'Creating...' : 'Create organization'}
45
+ </Button>
46
+ </form>
47
+ <p className="text-body text-text-secondary">
48
+ Already have a workspace?{' '}
49
+ <a className="font-semibold text-accent" href="/login">
50
+ Sign in
51
+ </a>
52
+ </p>
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,23 @@
1
+ const variants = {
2
+ success: 'bg-success/15 text-success',
3
+ warning: 'bg-warning/15 text-warning',
4
+ error: 'bg-error/15 text-error',
5
+ neutral: 'bg-border text-text-secondary',
6
+ accent: 'bg-accent/15 text-accent',
7
+ } as const;
8
+
9
+ export function Badge({
10
+ variant = 'neutral',
11
+ children,
12
+ }: {
13
+ variant?: keyof typeof variants;
14
+ children: React.ReactNode;
15
+ }) {
16
+ return (
17
+ <span
18
+ className={`inline-flex items-center rounded px-1.5 py-0.5 text-[11px] font-medium leading-4 ${variants[variant]}`}
19
+ >
20
+ {children}
21
+ </span>
22
+ );
23
+ }
@@ -0,0 +1,37 @@
1
+ import Link from 'next/link';
2
+ import { type ButtonHTMLAttributes, type ReactNode } from 'react';
3
+ import { twMerge } from 'tailwind-merge';
4
+
5
+ type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
6
+ variant?: 'primary' | 'secondary' | 'ghost';
7
+ href?: string;
8
+ children: ReactNode;
9
+ };
10
+
11
+ const variants = {
12
+ primary: 'bg-accent text-white hover:bg-accent-hover',
13
+ secondary: 'bg-surface-raised text-text-primary border border-border hover:border-border-focus',
14
+ ghost: 'bg-transparent text-text-secondary hover:bg-surface-raised hover:text-text-primary',
15
+ };
16
+
17
+ export function Button({ variant = 'primary', href, className, children, ...props }: ButtonProps) {
18
+ const classes = twMerge(
19
+ 'inline-flex min-h-10 items-center justify-center gap-2 rounded px-4 text-body font-medium transition duration-150 disabled:cursor-not-allowed disabled:opacity-60 active:scale-[0.98]',
20
+ variants[variant],
21
+ className,
22
+ );
23
+
24
+ if (href) {
25
+ return (
26
+ <Link className={classes} href={href}>
27
+ {children}
28
+ </Link>
29
+ );
30
+ }
31
+
32
+ return (
33
+ <button className={classes} {...props}>
34
+ {children}
35
+ </button>
36
+ );
37
+ }
@@ -0,0 +1,11 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
5
+ return (
6
+ <div
7
+ className={twMerge('rounded border border-border bg-surface p-5', className)}
8
+ {...props}
9
+ />
10
+ );
11
+ }
@@ -0,0 +1,77 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { X } from 'lucide-react';
5
+
6
+ export function ConfirmDialog({
7
+ title,
8
+ description,
9
+ confirmText,
10
+ confirmLabel = 'Confirm',
11
+ onConfirm,
12
+ onCancel,
13
+ destructive = false,
14
+ }: {
15
+ title: string;
16
+ description: string;
17
+ confirmText?: string;
18
+ confirmLabel?: string;
19
+ onConfirm: () => void;
20
+ onCancel: () => void;
21
+ destructive?: boolean;
22
+ }) {
23
+ const [input, setInput] = useState('');
24
+ const canConfirm = confirmText ? input === confirmText : true;
25
+
26
+ return (
27
+ <div className="fixed inset-0 z-50 flex items-center justify-center">
28
+ <div className="absolute inset-0 bg-black/60" onClick={onCancel} />
29
+ <div className="relative w-full max-w-md rounded-lg bg-surface border border-border p-5">
30
+ <button
31
+ onClick={onCancel}
32
+ className="absolute top-3 right-3 text-text-tertiary hover:text-text-secondary"
33
+ >
34
+ <X className="h-4 w-4" />
35
+ </button>
36
+
37
+ <h3 className="text-heading font-medium text-text-primary mb-2">{title}</h3>
38
+ <p className="text-body text-text-secondary mb-4">{description}</p>
39
+
40
+ {confirmText && (
41
+ <div className="mb-4">
42
+ <p className="text-body text-text-secondary mb-2">
43
+ Type <span className="font-mono text-text-primary">{confirmText}</span> to
44
+ confirm:
45
+ </p>
46
+ <input
47
+ value={input}
48
+ onChange={(e) => setInput(e.target.value)}
49
+ className="w-full rounded bg-bg border border-border px-3 py-2 text-body text-text-primary outline-none focus:border-border-focus"
50
+ autoFocus
51
+ />
52
+ </div>
53
+ )}
54
+
55
+ <div className="flex justify-end gap-2">
56
+ <button
57
+ onClick={onCancel}
58
+ className="rounded px-3 py-1.5 text-body text-text-secondary hover:bg-surface-raised transition-colors"
59
+ >
60
+ Cancel
61
+ </button>
62
+ <button
63
+ onClick={onConfirm}
64
+ disabled={!canConfirm}
65
+ className={`rounded px-3 py-1.5 text-body font-medium transition-colors disabled:opacity-40 ${
66
+ destructive
67
+ ? 'bg-error/15 text-error hover:bg-error/25'
68
+ : 'bg-accent text-white hover:bg-accent-hover'
69
+ }`}
70
+ >
71
+ {confirmLabel}
72
+ </button>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ );
77
+ }
@@ -0,0 +1,24 @@
1
+ import type { LucideIcon } from 'lucide-react';
2
+
3
+ export function EmptyState({
4
+ icon: Icon,
5
+ title,
6
+ description,
7
+ action,
8
+ }: {
9
+ icon: LucideIcon;
10
+ title: string;
11
+ description: string;
12
+ action?: React.ReactNode;
13
+ }) {
14
+ return (
15
+ <div className="flex flex-col items-center justify-center py-16 text-center">
16
+ <div className="rounded-lg bg-surface p-3 mb-4">
17
+ <Icon className="h-6 w-6 text-text-tertiary" />
18
+ </div>
19
+ <h3 className="text-heading font-medium text-text-primary mb-1">{title}</h3>
20
+ <p className="text-body text-text-secondary max-w-sm mb-4">{description}</p>
21
+ {action}
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,14 @@
1
+ import { type InputHTMLAttributes } from 'react';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function Input({ className, ...props }: InputHTMLAttributes<HTMLInputElement>) {
5
+ return (
6
+ <input
7
+ className={twMerge(
8
+ 'min-h-10 w-full rounded border border-border bg-surface px-3 text-body text-text-primary outline-none transition focus:border-border-focus',
9
+ className,
10
+ )}
11
+ {...props}
12
+ />
13
+ );
14
+ }
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+
3
+ import { useState, useCallback } from 'react';
4
+ import { Copy, Eye, EyeOff } from 'lucide-react';
5
+
6
+ export function KeyDisplay({ apiKey, masked = true }: { apiKey: string; masked?: boolean }) {
7
+ const [revealed, setRevealed] = useState(!masked);
8
+ const [copied, setCopied] = useState(false);
9
+
10
+ const maskedKey = apiKey.length > 8
11
+ ? `${apiKey.slice(0, 4)}${'•'.repeat(8)}${apiKey.slice(-4)}`
12
+ : '•'.repeat(apiKey.length);
13
+
14
+ const handleCopy = useCallback(() => {
15
+ navigator.clipboard.writeText(apiKey);
16
+ setCopied(true);
17
+ setTimeout(() => setCopied(false), 2000);
18
+ }, [apiKey]);
19
+
20
+ const handleReveal = useCallback(() => {
21
+ setRevealed(true);
22
+ setTimeout(() => setRevealed(false), 5000);
23
+ }, []);
24
+
25
+ return (
26
+ <div className="flex items-center gap-2 rounded bg-surface border border-border px-3 py-1.5 font-mono text-body">
27
+ <span className="flex-1 truncate text-text-secondary select-all">
28
+ {revealed ? apiKey : maskedKey}
29
+ </span>
30
+ {masked && (
31
+ <button
32
+ onClick={revealed ? () => setRevealed(false) : handleReveal}
33
+ className="text-text-tertiary hover:text-text-secondary transition-colors"
34
+ title={revealed ? 'Hide' : 'Reveal for 5 seconds'}
35
+ >
36
+ {revealed ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
37
+ </button>
38
+ )}
39
+ <button
40
+ onClick={handleCopy}
41
+ className="text-text-tertiary hover:text-text-secondary transition-colors"
42
+ title="Copy"
43
+ >
44
+ <Copy className="h-3.5 w-3.5" />
45
+ </button>
46
+ {copied && <span className="text-[11px] text-success">Copied</span>}
47
+ </div>
48
+ );
49
+ }
@@ -0,0 +1,14 @@
1
+ import { type SelectHTMLAttributes } from 'react';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function Select({ className, ...props }: SelectHTMLAttributes<HTMLSelectElement>) {
5
+ return (
6
+ <select
7
+ className={twMerge(
8
+ 'min-h-10 w-full rounded border border-border bg-surface px-3 text-body text-text-primary outline-none transition focus:border-border-focus',
9
+ className,
10
+ )}
11
+ {...props}
12
+ />
13
+ );
14
+ }
@@ -0,0 +1,24 @@
1
+ export function Skeleton({ className = '' }: { className?: string }) {
2
+ return <div className={`skeleton ${className}`} />;
3
+ }
4
+
5
+ export function SkeletonRow() {
6
+ return (
7
+ <div className="flex items-center gap-4 px-4 h-9">
8
+ <Skeleton className="h-3 w-24" />
9
+ <Skeleton className="h-3 w-32" />
10
+ <Skeleton className="h-3 w-16" />
11
+ <Skeleton className="h-3 w-20" />
12
+ </div>
13
+ );
14
+ }
15
+
16
+ export function SkeletonMetric() {
17
+ return (
18
+ <div className="rounded border border-border bg-surface p-4">
19
+ <Skeleton className="h-3 w-20 mb-3" />
20
+ <Skeleton className="h-6 w-12 mb-1" />
21
+ <Skeleton className="h-3 w-16" />
22
+ </div>
23
+ );
24
+ }