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,291 @@
1
+ 'use client';
2
+
3
+ import { usePathname } from 'next/navigation';
4
+ import { Activity, CalendarClock, FileText, GitBranch, Home, Settings, Users } from 'lucide-react';
5
+ import { useEffect, useState } from 'react';
6
+ import { clearSession } from '@/lib/storage';
7
+ import { createSupportTicket, getBillingStatus, logoutWorkspace, restoreSession } from '@/lib/api';
8
+ import { billingUpgradesEnabled } from '@/lib/billing';
9
+ import { useToast } from '@/components/ui/Toast';
10
+ import { AvailsyncLogo } from '@/components/brand/AvailsyncLogo';
11
+
12
+ const links = [
13
+ { href: '/app', label: 'Overview', icon: Home },
14
+ { href: '/app/agents', label: 'Agents', icon: Users },
15
+ { href: '/app/availability', label: 'Availability', icon: CalendarClock },
16
+ { href: '/app/work', label: 'Work Claims', icon: GitBranch },
17
+ { href: '/app/activity', label: 'API Activity', icon: Activity },
18
+ { href: '/app/audit', label: 'Audit', icon: FileText },
19
+ { href: '/app/settings', label: 'Settings', icon: Settings },
20
+ ];
21
+
22
+ export function AppShell({ children }: { children: React.ReactNode }) {
23
+ const pathname = usePathname();
24
+ const { toast } = useToast();
25
+ const isOnboarding = pathname.startsWith('/app/onboarding');
26
+ const [planInfo, setPlanInfo] = useState<{
27
+ plan: string;
28
+ agents_used: number;
29
+ agent_limit: number;
30
+ billing_email: string | null;
31
+ billing_upgrades_enabled: boolean;
32
+ } | null>(null);
33
+ const [userEmail, setUserEmail] = useState('');
34
+ const [checkingSession, setCheckingSession] = useState(!isOnboarding);
35
+ const [supportOpen, setSupportOpen] = useState(false);
36
+ const [supportSending, setSupportSending] = useState(false);
37
+ const [supportForm, setSupportForm] = useState({
38
+ email: '',
39
+ subject: '',
40
+ category: 'setup' as 'billing' | 'setup' | 'sdk' | 'agent_error' | 'bug' | 'other',
41
+ message: '',
42
+ });
43
+ const upgradesEnabled = planInfo?.billing_upgrades_enabled ?? billingUpgradesEnabled();
44
+
45
+ useEffect(() => {
46
+ if (isOnboarding) return;
47
+
48
+ restoreSession()
49
+ .then((session) => {
50
+ setUserEmail(session.user.email);
51
+ setSupportForm((current) => ({ ...current, email: current.email || session.user.email }));
52
+ return getBillingStatus();
53
+ })
54
+ .then((billing) => {
55
+ setPlanInfo(billing);
56
+ setSupportForm((current) => ({ ...current, email: current.email || billing.billing_email || userEmail }));
57
+ })
58
+ .catch(() => {
59
+ clearSession();
60
+ window.location.href = '/login';
61
+ })
62
+ .finally(() => setCheckingSession(false));
63
+ }, [isOnboarding]);
64
+
65
+ async function signOut() {
66
+ await logoutWorkspace().catch(() => {});
67
+ clearSession();
68
+ window.location.href = '/login';
69
+ }
70
+
71
+ async function submitSupportTicket() {
72
+ if (!supportForm.email || !supportForm.subject.trim() || !supportForm.message.trim()) {
73
+ toast('Fill email, subject, and message', 'error');
74
+ return;
75
+ }
76
+
77
+ setSupportSending(true);
78
+ try {
79
+ await createSupportTicket({
80
+ ...supportForm,
81
+ subject: supportForm.subject.trim(),
82
+ message: supportForm.message.trim(),
83
+ context: {
84
+ path: pathname,
85
+ app_url: window.location.origin,
86
+ },
87
+ });
88
+ toast('Support ticket received', 'success');
89
+ setSupportOpen(false);
90
+ setSupportForm({
91
+ email: supportForm.email,
92
+ subject: '',
93
+ category: 'setup',
94
+ message: '',
95
+ });
96
+ } catch (err) {
97
+ toast((err as Error).message, 'error');
98
+ } finally {
99
+ setSupportSending(false);
100
+ }
101
+ }
102
+
103
+ if (isOnboarding) {
104
+ return <main className="min-h-screen bg-bg text-text-primary">{children}</main>;
105
+ }
106
+
107
+ if (checkingSession) {
108
+ return <main className="min-h-screen bg-bg" />;
109
+ }
110
+
111
+ return (
112
+ <div className="flex min-h-dvh flex-col bg-bg text-text-primary lg:h-screen lg:flex-row">
113
+ <header className="sticky top-0 z-30 border-b border-border bg-surface lg:hidden">
114
+ <div className="flex items-center justify-between gap-3 px-4 py-3">
115
+ <a className="text-text-primary" href="/app">
116
+ <AvailsyncLogo markClassName="h-7 w-7" wordmarkClassName="h-[20px] sm:h-[22px]" />
117
+ </a>
118
+ <button
119
+ onClick={signOut}
120
+ className="min-h-10 rounded border border-border px-3 text-body text-text-secondary hover:text-text-primary"
121
+ >
122
+ Sign out
123
+ </button>
124
+ </div>
125
+ <nav className="dark-scroll flex gap-1 overflow-x-auto px-2 pb-2">
126
+ {links.map(({ href, label, icon: Icon }) => {
127
+ const isActive =
128
+ href === '/app' ? pathname === '/app' : pathname.startsWith(href);
129
+ return (
130
+ <a
131
+ className={`flex min-h-10 shrink-0 items-center gap-2 rounded px-3 text-body transition-colors duration-150 ${
132
+ isActive
133
+ ? 'bg-surface-raised text-text-primary'
134
+ : 'text-text-secondary hover:bg-surface-raised hover:text-text-primary'
135
+ }`}
136
+ href={href}
137
+ key={href}
138
+ >
139
+ <Icon className="h-4 w-4 flex-shrink-0" />
140
+ {label}
141
+ </a>
142
+ );
143
+ })}
144
+ </nav>
145
+ </header>
146
+
147
+ <aside className="hidden w-60 flex-shrink-0 flex-col overflow-y-auto border-r border-border bg-surface dark-scroll lg:flex">
148
+ <div className="px-4 py-4">
149
+ <a className="text-text-primary" href="/app">
150
+ <AvailsyncLogo markClassName="h-7 w-7" wordmarkClassName="h-[20px]" />
151
+ </a>
152
+ </div>
153
+
154
+ <div className="h-px bg-border mx-3" />
155
+
156
+ <nav className="flex-1 px-2 py-2 space-y-0.5">
157
+ {links.map(({ href, label, icon: Icon }) => {
158
+ const isActive =
159
+ href === '/app' ? pathname === '/app' : pathname.startsWith(href);
160
+ return (
161
+ <a
162
+ className={`flex items-center gap-2.5 rounded px-3 h-7.5 text-body transition-colors duration-150 ${
163
+ isActive
164
+ ? 'bg-surface-raised text-text-primary'
165
+ : 'text-text-secondary hover:bg-surface-raised hover:text-text-primary'
166
+ }`}
167
+ href={href}
168
+ key={href}
169
+ >
170
+ <Icon className="h-4 w-4 flex-shrink-0" />
171
+ {label}
172
+ </a>
173
+ );
174
+ })}
175
+ </nav>
176
+
177
+ <div className="h-px bg-border mx-3" />
178
+
179
+ <div className="px-4 py-3 text-label text-text-tertiary space-y-1">
180
+ <div>
181
+ Plan:{' '}
182
+ <span className="text-text-secondary capitalize">
183
+ {planInfo?.plan || 'Free'}
184
+ </span>
185
+ </div>
186
+ <div>
187
+ {planInfo ? `${planInfo.agents_used}/${planInfo.agent_limit}` : '0/3'} agents
188
+ </div>
189
+ {planInfo?.plan === 'free' && (
190
+ <a
191
+ href="/app/settings?tab=billing"
192
+ className="text-accent hover:text-accent-hover text-label inline-block mt-1"
193
+ >
194
+ {upgradesEnabled ? 'Upgrade' : 'Paid waitlist'} &rarr;
195
+ </a>
196
+ )}
197
+ <button
198
+ onClick={signOut}
199
+ className="block text-left text-text-tertiary hover:text-text-secondary text-label mt-2"
200
+ >
201
+ Sign out
202
+ </button>
203
+ </div>
204
+ </aside>
205
+
206
+ <main className="min-w-0 flex-1 overflow-y-auto bg-bg dark-scroll">
207
+ {children}
208
+ </main>
209
+ {planInfo && planInfo.plan !== 'free' && (
210
+ <>
211
+ <button
212
+ onClick={() => setSupportOpen(true)}
213
+ className="fixed bottom-4 right-4 z-40 rounded border border-border bg-accent px-4 py-2 text-body font-medium text-white shadow-lg hover:bg-accent-hover"
214
+ >
215
+ Support
216
+ </button>
217
+ {supportOpen && (
218
+ <div className="fixed inset-0 z-50 flex items-end justify-end bg-black/45 p-4 sm:items-center">
219
+ <div className="w-full max-w-md rounded border border-border bg-surface p-5 shadow-xl">
220
+ <div className="mb-4 flex items-start justify-between gap-4">
221
+ <div>
222
+ <h2 className="text-heading font-semibold text-text-primary">Contact support</h2>
223
+ <p className="mt-1 text-body text-text-secondary">
224
+ Send a ticket. We will reply directly by email.
225
+ </p>
226
+ </div>
227
+ <button
228
+ onClick={() => setSupportOpen(false)}
229
+ className="text-text-tertiary hover:text-text-primary"
230
+ >
231
+ Close
232
+ </button>
233
+ </div>
234
+ <div className="space-y-3">
235
+ <div>
236
+ <label className="mb-1 block text-label uppercase text-text-tertiary">Email</label>
237
+ <input
238
+ value={supportForm.email}
239
+ onChange={(event) => setSupportForm({ ...supportForm, email: event.target.value })}
240
+ className="w-full rounded border border-border bg-bg px-3 py-2 text-body text-text-primary outline-none focus:border-border-focus"
241
+ />
242
+ </div>
243
+ <div>
244
+ <label className="mb-1 block text-label uppercase text-text-tertiary">Category</label>
245
+ <select
246
+ value={supportForm.category}
247
+ onChange={(event) => setSupportForm({ ...supportForm, category: event.target.value as typeof supportForm.category })}
248
+ className="w-full rounded border border-border bg-bg px-3 py-2 text-body text-text-primary outline-none focus:border-border-focus"
249
+ >
250
+ <option value="setup">Setup</option>
251
+ <option value="sdk">SDK</option>
252
+ <option value="agent_error">Agent error</option>
253
+ <option value="bug">Bug</option>
254
+ <option value="billing">Billing</option>
255
+ <option value="other">Other</option>
256
+ </select>
257
+ </div>
258
+ <div>
259
+ <label className="mb-1 block text-label uppercase text-text-tertiary">Subject</label>
260
+ <input
261
+ value={supportForm.subject}
262
+ onChange={(event) => setSupportForm({ ...supportForm, subject: event.target.value })}
263
+ className="w-full rounded border border-border bg-bg px-3 py-2 text-body text-text-primary outline-none focus:border-border-focus"
264
+ placeholder="What do you need help with?"
265
+ />
266
+ </div>
267
+ <div>
268
+ <label className="mb-1 block text-label uppercase text-text-tertiary">Message</label>
269
+ <textarea
270
+ value={supportForm.message}
271
+ onChange={(event) => setSupportForm({ ...supportForm, message: event.target.value })}
272
+ className="min-h-32 w-full rounded border border-border bg-bg px-3 py-2 text-body text-text-primary outline-none focus:border-border-focus"
273
+ placeholder="Describe the issue, expected behavior, and what you already tried."
274
+ />
275
+ </div>
276
+ <button
277
+ onClick={submitSupportTicket}
278
+ disabled={supportSending}
279
+ className="w-full rounded bg-accent px-4 py-2 text-body font-medium text-white hover:bg-accent-hover disabled:opacity-50"
280
+ >
281
+ {supportSending ? 'Sending...' : 'Create ticket'}
282
+ </button>
283
+ </div>
284
+ </div>
285
+ </div>
286
+ )}
287
+ </>
288
+ )}
289
+ </div>
290
+ );
291
+ }
@@ -0,0 +1,117 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import { checkAvailability, listAgents } from '@/lib/api';
5
+ import { fromLocalInputValue, formatDateTime, toLocalInputValue } from '@/lib/format';
6
+ import { loadSession, type StoredSession } from '@/lib/storage';
7
+ import { Button } from '@/components/ui/Button';
8
+ import { Input } from '@/components/ui/Input';
9
+ import type { Agent, Slot } from '@/lib/schemas';
10
+
11
+ export function AvailabilityChecker() {
12
+ const [session, setSession] = useState<StoredSession | null>(null);
13
+ const [agents, setAgents] = useState<Agent[]>([]);
14
+ const [agentId, setAgentId] = useState('');
15
+ const [from, setFrom] = useState(toLocalInputValue(new Date(Date.now() + 24 * 60 * 60 * 1000)));
16
+ const [to, setTo] = useState(toLocalInputValue(new Date(Date.now() + 25 * 60 * 60 * 1000)));
17
+ const [duration, setDuration] = useState(30);
18
+ const [slots, setSlots] = useState<Slot[]>([]);
19
+ const [error, setError] = useState('');
20
+
21
+ useEffect(() => {
22
+ const loaded = loadSession();
23
+ setSession(loaded);
24
+ if (loaded) {
25
+ listAgents(loaded.orgId)
26
+ .then((rows) => {
27
+ setAgents(rows);
28
+ if (rows[0]) setAgentId(rows[0].id);
29
+ })
30
+ .catch(() => {});
31
+ }
32
+ }, []);
33
+
34
+ async function onSubmit(event: React.FormEvent) {
35
+ event.preventDefault();
36
+ if (!session) return;
37
+ setError('');
38
+ try {
39
+ const result = await checkAvailability({
40
+ agent_id: agentId,
41
+ from: fromLocalInputValue(from),
42
+ to: fromLocalInputValue(to),
43
+ duration_minutes: duration,
44
+ });
45
+ setSlots(result.slots);
46
+ } catch (err) {
47
+ setError(err instanceof Error ? err.message : 'Could not check availability');
48
+ }
49
+ }
50
+
51
+ if (!session) {
52
+ return <p className="text-body text-text-secondary">Create an agent in onboarding before checking availability.</p>;
53
+ }
54
+
55
+ return (
56
+ <div className="grid gap-6">
57
+ <form className="grid gap-4 rounded border border-border bg-surface p-5" onSubmit={onSubmit}>
58
+ <label className="text-label uppercase text-text-tertiary" htmlFor="availability-from">
59
+ From
60
+ </label>
61
+ <Input
62
+ id="availability-from"
63
+ type="datetime-local"
64
+ value={from}
65
+ onChange={(event) => setFrom(event.target.value)}
66
+ />
67
+ <label className="text-label uppercase text-text-tertiary" htmlFor="availability-agent">
68
+ Agent
69
+ </label>
70
+ <select
71
+ id="availability-agent"
72
+ value={agentId}
73
+ onChange={(event) => setAgentId(event.target.value)}
74
+ className="rounded border border-border bg-surface px-3 py-2 text-body text-text-primary"
75
+ >
76
+ {agents.map((agent) => (
77
+ <option key={agent.id} value={agent.id}>{agent.name}</option>
78
+ ))}
79
+ </select>
80
+ <label className="text-label uppercase text-text-tertiary" htmlFor="availability-to">
81
+ To
82
+ </label>
83
+ <Input
84
+ id="availability-to"
85
+ type="datetime-local"
86
+ value={to}
87
+ onChange={(event) => setTo(event.target.value)}
88
+ />
89
+ <label className="text-label uppercase text-text-tertiary" htmlFor="availability-duration">
90
+ Duration minutes
91
+ </label>
92
+ <Input
93
+ id="availability-duration"
94
+ max={480}
95
+ min={1}
96
+ type="number"
97
+ value={duration}
98
+ onChange={(event) => setDuration(Number(event.target.value))}
99
+ />
100
+ {error && <p className="text-body text-error">{error}</p>}
101
+ <Button type="submit">Check availability</Button>
102
+ </form>
103
+ <div className="grid gap-3">
104
+ {slots.map((slot) => (
105
+ <div className="rounded border border-border bg-surface p-4" key={slot.start}>
106
+ <p className="font-semibold text-text-primary">
107
+ {formatDateTime(slot.start)} - {formatDateTime(slot.end)}
108
+ </p>
109
+ <p className="mt-1 text-body text-text-secondary">
110
+ Confidence {slot.confidence} - Competing holds {slot.competing_holds_count}
111
+ </p>
112
+ </div>
113
+ ))}
114
+ </div>
115
+ </div>
116
+ );
117
+ }
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+
3
+ import { Input } from '@/components/ui/Input';
4
+
5
+ type AvailabilityWindowFormProps = {
6
+ startAt: string;
7
+ endAt: string;
8
+ onStartAtChange: (value: string) => void;
9
+ onEndAtChange: (value: string) => void;
10
+ };
11
+
12
+ export function AvailabilityWindowForm({
13
+ startAt,
14
+ endAt,
15
+ onStartAtChange,
16
+ onEndAtChange,
17
+ }: AvailabilityWindowFormProps) {
18
+ return (
19
+ <>
20
+ <label className="text-sm font-semibold" htmlFor="availability-starts">
21
+ Availability starts
22
+ </label>
23
+ <Input
24
+ id="availability-starts"
25
+ type="datetime-local"
26
+ value={startAt}
27
+ onChange={(event) => onStartAtChange(event.target.value)}
28
+ />
29
+ <label className="text-sm font-semibold" htmlFor="availability-ends">
30
+ Availability ends
31
+ </label>
32
+ <Input
33
+ id="availability-ends"
34
+ type="datetime-local"
35
+ value={endAt}
36
+ onChange={(event) => onEndAtChange(event.target.value)}
37
+ />
38
+ </>
39
+ );
40
+ }
@@ -0,0 +1,133 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useState } from 'react';
4
+ import { createHold, listAgents, listHolds, releaseHold } from '@/lib/api';
5
+ import { fromLocalInputValue, formatDateTime, toLocalInputValue } from '@/lib/format';
6
+ import { loadSession, type StoredSession } from '@/lib/storage';
7
+ import { Button } from '@/components/ui/Button';
8
+ import { Input } from '@/components/ui/Input';
9
+ import type { Agent, Hold } from '@/lib/schemas';
10
+
11
+ export function HoldForm() {
12
+ const [session, setSession] = useState<StoredSession | null>(null);
13
+ const [agentId, setAgentId] = useState('');
14
+ const [agents, setAgents] = useState<Agent[]>([]);
15
+ const [holds, setHolds] = useState<Hold[]>([]);
16
+ const [startAt, setStartAt] = useState(
17
+ toLocalInputValue(new Date(Date.now() + 24 * 60 * 60 * 1000)),
18
+ );
19
+ const [endAt, setEndAt] = useState(
20
+ toLocalInputValue(new Date(Date.now() + 24.5 * 60 * 60 * 1000)),
21
+ );
22
+ const [reason, setReason] = useState('Manual dashboard reservation');
23
+ const [error, setError] = useState('');
24
+
25
+ async function refresh(current: StoredSession) {
26
+ const agentRows = await listAgents(current.orgId);
27
+ setAgents(agentRows);
28
+ const nextAgentId = agentId || agentRows[0]?.id || '';
29
+ if (nextAgentId && !agentId) setAgentId(nextAgentId);
30
+ setHolds(await listHolds({ agent_id: nextAgentId || undefined }));
31
+ }
32
+
33
+ useEffect(() => {
34
+ const loaded = loadSession();
35
+ setSession(loaded);
36
+ if (loaded) {
37
+ refresh(loaded).catch(() => setError('Could not load holds'));
38
+ }
39
+ }, []);
40
+
41
+ async function onSubmit(event: React.FormEvent) {
42
+ event.preventDefault();
43
+ if (!session) return;
44
+ setError('');
45
+ try {
46
+ await createHold({
47
+ agent_id: agentId,
48
+ start_at: fromLocalInputValue(startAt),
49
+ end_at: fromLocalInputValue(endAt),
50
+ reason,
51
+ });
52
+ await refresh(session);
53
+ } catch (err) {
54
+ setError(err instanceof Error ? err.message : 'Could not create hold');
55
+ }
56
+ }
57
+
58
+ async function onRelease(holdId: string) {
59
+ if (!session) return;
60
+ await releaseHold(holdId);
61
+ await refresh(session);
62
+ }
63
+
64
+ if (!session) {
65
+ return <p className="text-body text-text-secondary">Create an agent before managing holds.</p>;
66
+ }
67
+
68
+ return (
69
+ <div className="grid gap-6">
70
+ <form className="grid gap-4 rounded border border-border bg-surface p-5" onSubmit={onSubmit}>
71
+ <label className="text-label uppercase text-text-tertiary" htmlFor="hold-start">
72
+ Start
73
+ </label>
74
+ <Input
75
+ id="hold-start"
76
+ type="datetime-local"
77
+ value={startAt}
78
+ onChange={(event) => setStartAt(event.target.value)}
79
+ />
80
+ <label className="text-label uppercase text-text-tertiary" htmlFor="hold-agent">
81
+ Agent
82
+ </label>
83
+ <select
84
+ id="hold-agent"
85
+ value={agentId}
86
+ onChange={(event) => setAgentId(event.target.value)}
87
+ className="rounded border border-border bg-surface px-3 py-2 text-body text-text-primary"
88
+ >
89
+ {agents.map((agent) => (
90
+ <option key={agent.id} value={agent.id}>{agent.name}</option>
91
+ ))}
92
+ </select>
93
+ <label className="text-label uppercase text-text-tertiary" htmlFor="hold-end">
94
+ End
95
+ </label>
96
+ <Input
97
+ id="hold-end"
98
+ type="datetime-local"
99
+ value={endAt}
100
+ onChange={(event) => setEndAt(event.target.value)}
101
+ />
102
+ <label className="text-label uppercase text-text-tertiary" htmlFor="hold-reason">
103
+ Reason
104
+ </label>
105
+ <Input id="hold-reason" value={reason} onChange={(event) => setReason(event.target.value)} />
106
+ {error && <p className="text-body text-error">{error}</p>}
107
+ <Button type="submit">Create hold</Button>
108
+ </form>
109
+ <div className="grid gap-3">
110
+ {holds.map((hold) => (
111
+ <div
112
+ className="flex flex-col justify-between gap-3 rounded border border-border bg-surface p-4 md:flex-row md:items-center"
113
+ key={hold.id}
114
+ >
115
+ <div>
116
+ <p className="font-semibold text-text-primary">
117
+ {formatDateTime(hold.start_at)} - {formatDateTime(hold.end_at)}
118
+ </p>
119
+ <p className="mt-1 text-body text-text-secondary">
120
+ {hold.status} - {hold.reason || 'No reason'}
121
+ </p>
122
+ </div>
123
+ {hold.status === 'confirmed' && (
124
+ <Button onClick={() => onRelease(hold.id)} variant="secondary">
125
+ Release
126
+ </Button>
127
+ )}
128
+ </div>
129
+ ))}
130
+ </div>
131
+ </div>
132
+ );
133
+ }
@@ -0,0 +1,10 @@
1
+ import { Card } from '@/components/ui/Card';
2
+
3
+ export function MetricCard({ label, value }: { label: string; value: string }) {
4
+ return (
5
+ <Card>
6
+ <p className="text-label uppercase text-text-tertiary">{label}</p>
7
+ <p className="mt-2 text-3xl font-semibold text-text-primary">{value}</p>
8
+ </Card>
9
+ );
10
+ }
@@ -0,0 +1,95 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { loginWorkspace, restoreSession } from '@/lib/api';
6
+ import { clearSession, saveSession } from '@/lib/storage';
7
+
8
+ export function LoginForm() {
9
+ const router = useRouter();
10
+ const [email, setEmail] = useState('');
11
+ const [password, setPassword] = useState('');
12
+ const [error, setError] = useState('');
13
+ const [loading, setLoading] = useState(false);
14
+
15
+ async function onSubmit(event: React.FormEvent) {
16
+ event.preventDefault();
17
+ setError('');
18
+ setLoading(true);
19
+ try {
20
+ clearSession();
21
+ await loginWorkspace(email, password);
22
+ const restored = await restoreSession();
23
+ saveSession({
24
+ orgId: restored.org.id,
25
+ userId: restored.user.id,
26
+ email: restored.user.email,
27
+ });
28
+ router.push('/app');
29
+ } catch (err) {
30
+ setError(err instanceof Error ? err.message : 'Could not sign in');
31
+ } finally {
32
+ setLoading(false);
33
+ }
34
+ }
35
+
36
+ return (
37
+ <div className="min-h-screen bg-bg flex items-center justify-center p-6">
38
+ <div className="w-full max-w-sm">
39
+ <a
40
+ href="/"
41
+ className="mb-6 inline-block text-body text-text-tertiary transition-colors hover:text-text-secondary"
42
+ >
43
+ Back to homepage
44
+ </a>
45
+ <h1 className="text-title font-semibold text-text-primary mb-2">Workspace login</h1>
46
+ <p className="text-body text-text-secondary mb-6">Sign in with your dashboard email and password.</p>
47
+ <form onSubmit={onSubmit} className="space-y-4">
48
+ <div>
49
+ <label className="block text-label uppercase text-text-tertiary mb-1" htmlFor="email">
50
+ Email
51
+ </label>
52
+ <input
53
+ id="email"
54
+ required
55
+ type="email"
56
+ value={email}
57
+ onChange={(e) => setEmail(e.target.value)}
58
+ className="w-full rounded bg-surface border border-border px-3 py-2.5 text-body text-text-primary outline-none focus:border-border-focus"
59
+ placeholder="you@company.com"
60
+ autoFocus
61
+ />
62
+ </div>
63
+ <div>
64
+ <label className="block text-label uppercase text-text-tertiary mb-1" htmlFor="password">
65
+ Password
66
+ </label>
67
+ <input
68
+ id="password"
69
+ required
70
+ type="password"
71
+ value={password}
72
+ onChange={(e) => setPassword(e.target.value)}
73
+ className="w-full rounded bg-surface border border-border px-3 py-2.5 text-body text-text-primary outline-none focus:border-border-focus"
74
+ placeholder="Password"
75
+ />
76
+ </div>
77
+ {error && <p className="text-body text-error">{error}</p>}
78
+ <button
79
+ disabled={loading}
80
+ type="submit"
81
+ className="w-full rounded bg-accent text-white py-2.5 text-body font-medium hover:bg-accent-hover transition-colors disabled:opacity-40 active:scale-[0.98]"
82
+ >
83
+ {loading ? 'Signing in...' : 'Sign in'}
84
+ </button>
85
+ <a
86
+ href="/signup"
87
+ className="block text-center text-body text-text-secondary hover:text-text-primary transition-colors"
88
+ >
89
+ Create workspace instead
90
+ </a>
91
+ </form>
92
+ </div>
93
+ </div>
94
+ );
95
+ }