@cdmbase/wiki-browser 12.0.18-alpha.5

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 (367) hide show
  1. package/LICENSE +21 -0
  2. package/lib/components/Logo.d.ts +4 -0
  3. package/lib/components/Logo.d.ts.map +1 -0
  4. package/lib/components/Logo.js +16 -0
  5. package/lib/components/Logo.js.map +1 -0
  6. package/lib/components/help/SidebarSearch.d.ts +8 -0
  7. package/lib/components/help/SidebarSearch.d.ts.map +1 -0
  8. package/lib/components/help/SidebarSearch.js +111 -0
  9. package/lib/components/help/SidebarSearch.js.map +1 -0
  10. package/lib/components/help/index.d.ts +2 -0
  11. package/lib/components/help/index.d.ts.map +1 -0
  12. package/lib/components/landing/FeatureCard.d.ts +13 -0
  13. package/lib/components/landing/FeatureCard.d.ts.map +1 -0
  14. package/lib/components/landing/FeatureCard.js +85 -0
  15. package/lib/components/landing/FeatureCard.js.map +1 -0
  16. package/lib/components/landing/QuickLinkCard.d.ts +8 -0
  17. package/lib/components/landing/QuickLinkCard.d.ts.map +1 -0
  18. package/lib/components/landing/QuickLinkCard.js +26 -0
  19. package/lib/components/landing/QuickLinkCard.js.map +1 -0
  20. package/lib/components/landing/SearchInput.d.ts +10 -0
  21. package/lib/components/landing/SearchInput.d.ts.map +1 -0
  22. package/lib/components/landing/SearchInput.js +223 -0
  23. package/lib/components/landing/SearchInput.js.map +1 -0
  24. package/lib/components/landing/index.d.ts +4 -0
  25. package/lib/components/landing/index.d.ts.map +1 -0
  26. package/lib/components/welcome.d.ts +3 -0
  27. package/lib/components/welcome.d.ts.map +1 -0
  28. package/lib/compute.d.ts +4 -0
  29. package/lib/compute.d.ts.map +1 -0
  30. package/lib/compute.js +96 -0
  31. package/lib/compute.js.map +1 -0
  32. package/lib/config/env-config.d.ts +4 -0
  33. package/lib/config/env-config.d.ts.map +1 -0
  34. package/lib/config/env-config.js +7 -0
  35. package/lib/config/env-config.js.map +1 -0
  36. package/lib/docs.config.d.ts +48 -0
  37. package/lib/docs.config.d.ts.map +1 -0
  38. package/lib/index.d.ts +4 -0
  39. package/lib/index.d.ts.map +1 -0
  40. package/lib/index.js +2 -0
  41. package/lib/index.js.map +1 -0
  42. package/lib/loaders/search.d.ts +1 -0
  43. package/lib/loaders/search.d.ts.map +1 -0
  44. package/lib/module.d.ts +4 -0
  45. package/lib/module.d.ts.map +1 -0
  46. package/lib/module.js +11 -0
  47. package/lib/module.js.map +1 -0
  48. package/lib/pages/ArticlePage/ArticlePage.d.ts +4 -0
  49. package/lib/pages/ArticlePage/ArticlePage.d.ts.map +1 -0
  50. package/lib/pages/ArticlePage/ArticlePage.js +222 -0
  51. package/lib/pages/ArticlePage/ArticlePage.js.map +1 -0
  52. package/lib/pages/ArticlePage/index.d.ts +3 -0
  53. package/lib/pages/ArticlePage/index.d.ts.map +1 -0
  54. package/lib/pages/ArticlePage/index.js +3 -0
  55. package/lib/pages/ArticlePage/index.js.map +1 -0
  56. package/lib/pages/CategoryCollection/CategoryCollection.d.ts +4 -0
  57. package/lib/pages/CategoryCollection/CategoryCollection.d.ts.map +1 -0
  58. package/lib/pages/CategoryCollection/CategoryCollection.js +103 -0
  59. package/lib/pages/CategoryCollection/CategoryCollection.js.map +1 -0
  60. package/lib/pages/CategoryCollection/index.d.ts +3 -0
  61. package/lib/pages/CategoryCollection/index.d.ts.map +1 -0
  62. package/lib/pages/CategoryCollection/index.js +3 -0
  63. package/lib/pages/CategoryCollection/index.js.map +1 -0
  64. package/lib/pages/Help/HelpIndex.d.ts +4 -0
  65. package/lib/pages/Help/HelpIndex.d.ts.map +1 -0
  66. package/lib/pages/Help/HelpIndex.js +44 -0
  67. package/lib/pages/Help/HelpIndex.js.map +1 -0
  68. package/lib/pages/Help/index.d.ts +4 -0
  69. package/lib/pages/Help/index.d.ts.map +1 -0
  70. package/lib/pages/Help/index.js +226 -0
  71. package/lib/pages/Help/index.js.map +1 -0
  72. package/lib/pages/Landing/index.d.ts +3 -0
  73. package/lib/pages/Landing/index.d.ts.map +1 -0
  74. package/lib/pages/Landing/index.js +281 -0
  75. package/lib/pages/Landing/index.js.map +1 -0
  76. package/lib/routes.json +2533 -0
  77. package/lib/seo.d.ts +22 -0
  78. package/lib/seo.d.ts.map +1 -0
  79. package/lib/slot-fill/FooterFill.d.ts +3 -0
  80. package/lib/slot-fill/FooterFill.d.ts.map +1 -0
  81. package/lib/slot-fill/FooterFill.js +18 -0
  82. package/lib/slot-fill/FooterFill.js.map +1 -0
  83. package/lib/slot-fill/LogoFill.d.ts +5 -0
  84. package/lib/slot-fill/LogoFill.d.ts.map +1 -0
  85. package/lib/slot-fill/LogoFill.js +74 -0
  86. package/lib/slot-fill/LogoFill.js.map +1 -0
  87. package/lib/slot-fill/consts.d.ts +5 -0
  88. package/lib/slot-fill/consts.d.ts.map +1 -0
  89. package/lib/slot-fill/consts.js +1 -0
  90. package/lib/slot-fill/consts.js.map +1 -0
  91. package/lib/slot-fill/index.d.ts +4 -0
  92. package/lib/slot-fill/index.d.ts.map +1 -0
  93. package/lib/templates/assets/images/add-link-frontend.png +0 -0
  94. package/lib/templates/assets/images/add-package-backend.png +0 -0
  95. package/lib/templates/assets/images/add-to-backend-module.png +0 -0
  96. package/lib/templates/assets/images/add-upload-client-frontend.png +0 -0
  97. package/lib/templates/assets/images/additional-parameters.png +0 -0
  98. package/lib/templates/assets/images/aeh-implementation.png +0 -0
  99. package/lib/templates/assets/images/aeh-usage.png +0 -0
  100. package/lib/templates/assets/images/apollo-client/recommendation_cache_mgmt.png +0 -0
  101. package/lib/templates/assets/images/app-deploy-new-version/jenkins1.PNG +0 -0
  102. package/lib/templates/assets/images/app-deploy-new-version/jenkins2.PNG +0 -0
  103. package/lib/templates/assets/images/auth-wrapper-code.png +0 -0
  104. package/lib/templates/assets/images/cdebase.png +0 -0
  105. package/lib/templates/assets/images/cdm-locales-directory.png +0 -0
  106. package/lib/templates/assets/images/client-settings.png +0 -0
  107. package/lib/templates/assets/images/codegen_file_update.png +0 -0
  108. package/lib/templates/assets/images/configuration.png +0 -0
  109. package/lib/templates/assets/images/copy-plugin.png +0 -0
  110. package/lib/templates/assets/images/docusaurus.png +0 -0
  111. package/lib/templates/assets/images/error-link.png +0 -0
  112. package/lib/templates/assets/images/error-sample.png +0 -0
  113. package/lib/templates/assets/images/extension copy.png +0 -0
  114. package/lib/templates/assets/images/extension.png +0 -0
  115. package/lib/templates/assets/images/graphql/graphql-folder-backend.png +0 -0
  116. package/lib/templates/assets/images/graphql/graphql-folder-with-gql.png +0 -0
  117. package/lib/templates/assets/images/i18n-config.png +0 -0
  118. package/lib/templates/assets/images/image.png +0 -0
  119. package/lib/templates/assets/images/logo.svg +10 -0
  120. package/lib/templates/assets/images/logo1.svg +1 -0
  121. package/lib/templates/assets/images/modify-upload-false-server.png +0 -0
  122. package/lib/templates/assets/images/navigation-auth-enabled.png +0 -0
  123. package/lib/templates/assets/images/org-dashboard-navigation.png +0 -0
  124. package/lib/templates/assets/images/org-navigation.png +0 -0
  125. package/lib/templates/assets/images/preferences_graphql_type.png +0 -0
  126. package/lib/templates/assets/images/provider.png +0 -0
  127. package/lib/templates/assets/images/route-config.png +0 -0
  128. package/lib/templates/assets/images/service-accounts.png +0 -0
  129. package/lib/templates/assets/images/source-code/source-code-environments.png +0 -0
  130. package/lib/templates/assets/images/source-code/source-code-organization.png +0 -0
  131. package/lib/templates/assets/images/spin-clone-develop-deployment/jenkins-changes.png +0 -0
  132. package/lib/templates/assets/images/spin-clone-develop-deployment/lerna-changes.png +0 -0
  133. package/lib/templates/assets/images/spin-clone-develop-deployment/root-package-json-changes.png +0 -0
  134. package/lib/templates/assets/images/spin-clone-develop-deployment/values-dev-changes.png +0 -0
  135. package/lib/templates/assets/images/sso-mappers.png +0 -0
  136. package/lib/templates/assets/images/sso-picture-mapper.png +0 -0
  137. package/lib/templates/assets/images/sso-settings.png +0 -0
  138. package/lib/templates/assets/images/timesheet_apollo_cache.png +0 -0
  139. package/lib/templates/assets/images/timesheet_query.png +0 -0
  140. package/lib/templates/assets/images/tutorial/docsVersionDropdown.png +0 -0
  141. package/lib/templates/assets/images/tutorial/localeDropdown.png +0 -0
  142. package/lib/templates/assets/images/unauthenticated.png +0 -0
  143. package/lib/templates/assets/images/undraw_docusaurus_mountain.svg +170 -0
  144. package/lib/templates/assets/images/undraw_docusaurus_react.svg +169 -0
  145. package/lib/templates/assets/images/undraw_docusaurus_tree.svg +1 -0
  146. package/lib/templates/assets/images/vite-plugin-config.png +0 -0
  147. package/lib/templates/content/docs/Generators/Project/generate-fullproject.md +12 -0
  148. package/lib/templates/content/docs/LLM/Logger.llm.md +194 -0
  149. package/lib/templates/content/docs/LLM/backend-proxies-services-llm.md +2687 -0
  150. package/lib/templates/content/docs/LLM/backend-service-llm.md +3384 -0
  151. package/lib/templates/content/docs/LLM/db_migration_llm.md +954 -0
  152. package/lib/templates/content/docs/LLM/frontend/REMIX-15.3-upgrade-llm.md +1245 -0
  153. package/lib/templates/content/docs/LLM/inngest/INNGEST_FUNCTION_DEVELOPMENT_GUIDE_LLM.md +1241 -0
  154. package/lib/templates/content/docs/LLM/inngest/INNGEST_NAMESPACE_LLM.md +384 -0
  155. package/lib/templates/content/docs/LLM/llm_workflow_namespace.md +384 -0
  156. package/lib/templates/content/docs/LLM/organization-components-form-llm.md +1395 -0
  157. package/lib/templates/content/docs/LLM/page-component-llm.md +173 -0
  158. package/lib/templates/content/docs/LLM/preferences-settings-llm.md +2781 -0
  159. package/lib/templates/content/docs/LLM/tailwind-css-llm.md +502 -0
  160. package/lib/templates/content/docs/UI/SchemaBasedUI.md +334 -0
  161. package/lib/templates/content/docs/UI/SlotFillComponent.md +334 -0
  162. package/lib/templates/content/docs/adminide-modules/account/auth0-login.md +31 -0
  163. package/lib/templates/content/docs/adminide-modules/account/index.md +14 -0
  164. package/lib/templates/content/docs/adminide-modules/account/keycloak-remix-setup.md +86 -0
  165. package/lib/templates/content/docs/adminide-modules/account/remix-auth-setup.md +79 -0
  166. package/lib/templates/content/docs/adminide-modules/account/various-auth-qatest.md +157 -0
  167. package/lib/templates/content/docs/adminide-modules/api-builders/graphql.md +906 -0
  168. package/lib/templates/content/docs/adminide-modules/billing/payments/index.md +14 -0
  169. package/lib/templates/content/docs/adminide-modules/billing/payments/stripe/index.md +14 -0
  170. package/lib/templates/content/docs/adminide-modules/billing/payments/stripe/settingup-stripe-locally.md +25 -0
  171. package/lib/templates/content/docs/adminide-modules/billing/tier-config.md +293 -0
  172. package/lib/templates/content/docs/adminide-modules/connectors/Connector.md +207 -0
  173. package/lib/templates/content/docs/adminide-modules/file-upload/index.md +16 -0
  174. package/lib/templates/content/docs/adminide-modules/file-upload/setup.md +435 -0
  175. package/lib/templates/content/docs/adminide-modules/file-upload/upload-file-using-signed-url.md +161 -0
  176. package/lib/templates/content/docs/adminide-modules/preferences/AddAdditionalPermissions.md +151 -0
  177. package/lib/templates/content/docs/adminide-modules/preferences/Configuration.md +241 -0
  178. package/lib/templates/content/docs/adminide-modules/preferences/Policy-Configuration.md +61 -0
  179. package/lib/templates/content/docs/adminide-modules/preferences/UI-components/ResourceSettingsLoader.md +319 -0
  180. package/lib/templates/content/docs/adminide-modules/preferences/contribute_scope_target.md +280 -0
  181. package/lib/templates/content/docs/adminide-modules/preferences/generate-urii.md +94 -0
  182. package/lib/templates/content/docs/adminide-modules/preferences/index.md +28 -0
  183. package/lib/templates/content/docs/adminide-modules/preferences/machine-configuration.md +157 -0
  184. package/lib/templates/content/docs/adminide-modules/preferences/pageSettings/generateCdecodeUri.md +1289 -0
  185. package/lib/templates/content/docs/adminide-modules/preferences/pageSettings/migratingFromUseSettings.md +215 -0
  186. package/lib/templates/content/docs/adminide-modules/preferences/permissions/Roles-Permissions.md +72 -0
  187. package/lib/templates/content/docs/adminide-modules/preferences/permissions/settingUserPermissions.md +139 -0
  188. package/lib/templates/content/docs/adminide-modules/preferences/preference-dependency.md +138 -0
  189. package/lib/templates/content/docs/adminide-modules/preferences/route-based-configuration.md +41 -0
  190. package/lib/templates/content/docs/adminide-modules/preferences/schema-configuration.md +71 -0
  191. package/lib/templates/content/docs/adminide-modules/preferences/supported.md +24 -0
  192. package/lib/templates/content/docs/adminide-modules/preferences/useSettingsLoader.md +248 -0
  193. package/lib/templates/content/docs/adminide-modules/project-tools/auth-providers.md +1317 -0
  194. package/lib/templates/content/docs/adminide-modules/project-tools/keycloak-guide.md +543 -0
  195. package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenant-based-authentication.md +846 -0
  196. package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenant-management.md +708 -0
  197. package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenants.md +1117 -0
  198. package/lib/templates/content/docs/chrome-extension/index.md +14 -0
  199. package/lib/templates/content/docs/chrome-extension/setup.md +30 -0
  200. package/lib/templates/content/docs/contributing/adding-package.md +23 -0
  201. package/lib/templates/content/docs/contributing/adding_new_modules.md +99 -0
  202. package/lib/templates/content/docs/contributing/architecture-updates.md +19 -0
  203. package/lib/templates/content/docs/contributing/avoid-using-promises-ui.md +116 -0
  204. package/lib/templates/content/docs/contributing/coding-guidelines.md +111 -0
  205. package/lib/templates/content/docs/contributing/do-and-dont.md +42 -0
  206. package/lib/templates/content/docs/contributing/faq.md +22 -0
  207. package/lib/templates/content/docs/contributing/folder-setup/browser.md +12 -0
  208. package/lib/templates/content/docs/contributing/folder-setup/config.md +12 -0
  209. package/lib/templates/content/docs/contributing/folder-setup/containers-server.md +12 -0
  210. package/lib/templates/content/docs/contributing/folder-setup/core.md +12 -0
  211. package/lib/templates/content/docs/contributing/folder-setup/graphql.md +12 -0
  212. package/lib/templates/content/docs/contributing/folder-setup/index.md +30 -0
  213. package/lib/templates/content/docs/contributing/folder-setup/module.md +12 -0
  214. package/lib/templates/content/docs/contributing/folder-setup/server.md +12 -0
  215. package/lib/templates/content/docs/contributing/folder-setup/services.md +12 -0
  216. package/lib/templates/content/docs/contributing/folder-setup/store.md +12 -0
  217. package/lib/templates/content/docs/contributing/frontend-coding.md +30 -0
  218. package/lib/templates/content/docs/contributing/git-subtree-sharing.md +73 -0
  219. package/lib/templates/content/docs/contributing/graphql-subscriptions.md +69 -0
  220. package/lib/templates/content/docs/contributing/how-to-contribute.md +30 -0
  221. package/lib/templates/content/docs/contributing/how_to_check_pure_esm.md +29 -0
  222. package/lib/templates/content/docs/contributing/index.md +60 -0
  223. package/lib/templates/content/docs/contributing/installation-issues.md +23 -0
  224. package/lib/templates/content/docs/contributing/keyboard-shortcut.md +131 -0
  225. package/lib/templates/content/docs/contributing/language/locale-support.md +12 -0
  226. package/lib/templates/content/docs/contributing/lerna-build-tools.md +516 -0
  227. package/lib/templates/content/docs/contributing/lerna-yarn-workspaces.md +95 -0
  228. package/lib/templates/content/docs/contributing/lint-and-formatter.md +20 -0
  229. package/lib/templates/content/docs/contributing/mobile-setup.md +16 -0
  230. package/lib/templates/content/docs/contributing/project-setup.md +233 -0
  231. package/lib/templates/content/docs/contributing/react/index.md +14 -0
  232. package/lib/templates/content/docs/contributing/react/lazy-component.md +70 -0
  233. package/lib/templates/content/docs/contributing/run-various-options.md +124 -0
  234. package/lib/templates/content/docs/contributing/schema-first-graphql-types.md +37 -0
  235. package/lib/templates/content/docs/contributing/source-code-organization.md +57 -0
  236. package/lib/templates/content/docs/contributing/staging-docker.md +88 -0
  237. package/lib/templates/content/docs/contributing/third-party/apollo-client-v3-tutorials.md +28 -0
  238. package/lib/templates/content/docs/contributing/third-party/index.md +18 -0
  239. package/lib/templates/content/docs/contributing/typescript-contribution.md +16 -0
  240. package/lib/templates/content/docs/devops/app-deploy-new-version.md +30 -0
  241. package/lib/templates/content/docs/devops/index.md +14 -0
  242. package/lib/templates/content/docs/devops/mobile-jenkins-build.md +40 -0
  243. package/lib/templates/content/docs/devops/versioning-the-project.md +128 -0
  244. package/lib/templates/content/docs/error-handler/application-error-handler.md +40 -0
  245. package/lib/templates/content/docs/error-handler/error-handling.md +26 -0
  246. package/lib/templates/content/docs/error-handler/index.md +16 -0
  247. package/lib/templates/content/docs/error-handler/logging-errors.md +14 -0
  248. package/lib/templates/content/docs/feature-api/copy-operation.md +427 -0
  249. package/lib/templates/content/docs/feature-api/feature-browser/assets.md +46 -0
  250. package/lib/templates/content/docs/feature-api/feature-browser/auth-permissions.md +12 -0
  251. package/lib/templates/content/docs/feature-api/feature-browser/feature.md +131 -0
  252. package/lib/templates/content/docs/feature-api/feature-browser/index.md +22 -0
  253. package/lib/templates/content/docs/feature-api/feature-browser/routes-menu.md +110 -0
  254. package/lib/templates/content/docs/feature-api/feature-browser/routing-convention.md +124 -0
  255. package/lib/templates/content/docs/feature-api/feature-browser/routing.md +338 -0
  256. package/lib/templates/content/docs/feature-api/feature-mobile/auth-permissions.md +20 -0
  257. package/lib/templates/content/docs/feature-api/feature-mobile/feature.md +130 -0
  258. package/lib/templates/content/docs/feature-api/feature-mobile/index.md +18 -0
  259. package/lib/templates/content/docs/feature-api/feature-mobile/navigation.md +187 -0
  260. package/lib/templates/content/docs/feature-api/feature-server/Scheduling.md +44 -0
  261. package/lib/templates/content/docs/feature-api/feature-server/dataloader.md +320 -0
  262. package/lib/templates/content/docs/feature-api/feature-server/dependency-injection.md +81 -0
  263. package/lib/templates/content/docs/feature-api/feature-server/feature.md +65 -0
  264. package/lib/templates/content/docs/feature-api/feature-server/generic-dataloader.md +135 -0
  265. package/lib/templates/content/docs/feature-api/feature-server/index.md +40 -0
  266. package/lib/templates/content/docs/feature-api/feature-server/migration.md +127 -0
  267. package/lib/templates/content/docs/feature-api/feature-server/mongo-model.md +72 -0
  268. package/lib/templates/content/docs/feature-api/feature-server/permissions.md +12 -0
  269. package/lib/templates/content/docs/feature-api/feature-server/policies.md +57 -0
  270. package/lib/templates/content/docs/feature-api/feature-server/preferences.md +57 -0
  271. package/lib/templates/content/docs/feature-api/feature-server/repositories.md +114 -0
  272. package/lib/templates/content/docs/feature-api/feature-server/resolvers.md +126 -0
  273. package/lib/templates/content/docs/feature-api/feature-server/rules.md +132 -0
  274. package/lib/templates/content/docs/feature-api/feature-server/schema.md +12 -0
  275. package/lib/templates/content/docs/feature-api/feature-server/services.md +102 -0
  276. package/lib/templates/content/docs/feature-api/feature-server/setup-resource-crud.md +359 -0
  277. package/lib/templates/content/docs/feature-api/index.md +18 -0
  278. package/lib/templates/content/docs/graphql/apolloClient-mutation.md +94 -0
  279. package/lib/templates/content/docs/graphql/index.md +14 -0
  280. package/lib/templates/content/docs/graphql/scalars.md +15 -0
  281. package/lib/templates/content/docs/help/index.md +14 -0
  282. package/lib/templates/content/docs/help/intro.md +16 -0
  283. package/lib/templates/content/docs/intl/ant-design-menu-translation.md +74 -0
  284. package/lib/templates/content/docs/intl/intl-namespace.md +129 -0
  285. package/lib/templates/content/docs/intl/vite-plugin-intl.md +87 -0
  286. package/lib/templates/content/docs/intl/webpack-plugin-intl.md +12 -0
  287. package/lib/templates/content/docs/intro.md +18 -0
  288. package/lib/templates/content/docs/knowledge/basic-fullstack.md +238 -0
  289. package/lib/templates/content/docs/mailing/index.md +14 -0
  290. package/lib/templates/content/docs/mailing/mailing-template.md +148 -0
  291. package/lib/templates/content/docs/mobile/App-navigation-generator.md +410 -0
  292. package/lib/templates/content/docs/mobile/MobileTestCases.md +264 -0
  293. package/lib/templates/content/docs/mobile/eas-profile-build.md +107 -0
  294. package/lib/templates/content/docs/mobile/expo-push-notification-setup.md +216 -0
  295. package/lib/templates/content/docs/mobile/index.md +14 -0
  296. package/lib/templates/content/docs/mobile/routes.md +83 -0
  297. package/lib/templates/content/docs/organization/adding-account-context.md +116 -0
  298. package/lib/templates/content/docs/organization/adding-org-mobile-navigation.md +22 -0
  299. package/lib/templates/content/docs/organization/adding-org-web-navigation.md +12 -0
  300. package/lib/templates/content/docs/organization/index.md +20 -0
  301. package/lib/templates/content/docs/organization/initialization.md +20 -0
  302. package/lib/templates/content/docs/organization/organization-resource-vs-resource.md +112 -0
  303. package/lib/templates/content/docs/remix/configuration/component-structure-best-practices.md +152 -0
  304. package/lib/templates/content/docs/remix/configuration/configurations.md +218 -0
  305. package/lib/templates/content/docs/remix/configuration/css-import-and-stylesheets.md +142 -0
  306. package/lib/templates/content/docs/remix/configuration/dont-subcomponent-network.md +166 -0
  307. package/lib/templates/content/docs/remix/configuration/generated-data-loaders.md +122 -0
  308. package/lib/templates/content/docs/remix/configuration/generated-resource-loaders.md +257 -0
  309. package/lib/templates/content/docs/remix/configuration/query-params-generator.md +216 -0
  310. package/lib/templates/content/docs/remix/configuration/routes-extra-icons.md +103 -0
  311. package/lib/templates/content/docs/remix/configuration/routes-json-advanced.md +86 -0
  312. package/lib/templates/content/docs/remix/configuration/routes-json-auth.md +113 -0
  313. package/lib/templates/content/docs/remix/configuration/routes-json-best-practices.md +55 -0
  314. package/lib/templates/content/docs/remix/configuration/routes-json-fields.md +79 -0
  315. package/lib/templates/content/docs/remix/configuration/routes-json-graphql.md +79 -0
  316. package/lib/templates/content/docs/remix/configuration/routes-json-index.md +112 -0
  317. package/lib/templates/content/docs/remix/configuration/routes-json-loaders.md +165 -0
  318. package/lib/templates/content/docs/remix/configuration/routes-json-middleware.md +196 -0
  319. package/lib/templates/content/docs/remix/configuration/routes-json-overview.md +53 -0
  320. package/lib/templates/content/docs/remix/data-loaders.md +43 -0
  321. package/lib/templates/content/docs/remix/devtools/remix-devtools.md +58 -0
  322. package/lib/templates/content/docs/remix/examples/changes-using-servercode.md +79 -0
  323. package/lib/templates/content/docs/remix/extra-icons.md +62 -0
  324. package/lib/templates/content/docs/remix/extra-links.md +65 -0
  325. package/lib/templates/content/docs/remix/generated-data-loaders.md +114 -0
  326. package/lib/templates/content/docs/remix/queryParamsGenerator.md +89 -0
  327. package/lib/templates/content/docs/remix/resources.md +16 -0
  328. package/lib/templates/content/docs/remix/styles.md +132 -0
  329. package/lib/templates/content/docs/remix/wiki.md +12 -0
  330. package/lib/templates/content/docs/security/auth-wrapper/auth-wrapper.md +24 -0
  331. package/lib/templates/content/docs/security/index.md +18 -0
  332. package/lib/templates/content/docs/security/secure-button-mobilenative.md +88 -0
  333. package/lib/templates/content/docs/security/secure-button-web.md +89 -0
  334. package/lib/templates/content/docs/server-side/account-customization.md +82 -0
  335. package/lib/templates/content/docs/server-side/apollo/caching.md +164 -0
  336. package/lib/templates/content/docs/server-side/backend-architecture/FINAL-DECISION.md +209 -0
  337. package/lib/templates/content/docs/server-side/backend-architecture/TRUE-FINAL-ARCHITECTURE.md +603 -0
  338. package/lib/templates/content/docs/server-side/backend-architecture/index1.md +0 -0
  339. package/lib/templates/content/docs/server-side/backend-coding.md +839 -0
  340. package/lib/templates/content/docs/server-side/e2b/manageing-template.md +197 -0
  341. package/lib/templates/content/docs/server-side/index.md +14 -0
  342. package/lib/templates/content/docs/server-side/inngest-functions-module.md +309 -0
  343. package/lib/templates/content/docs/server-side/listen-stripe-events.md +43 -0
  344. package/lib/templates/content/docs/server-side/slug-service.md +323 -0
  345. package/lib/templates/content/docs/tests/index.md +18 -0
  346. package/lib/templates/content/docs/tests/jest-test-debug-vscode.md +40 -0
  347. package/lib/templates/content/docs/tests/known-errors.md +116 -0
  348. package/lib/templates/content/docs/tests/service-test-template.md +118 -0
  349. package/lib/templates/content/docs/tests/test-setup.md +93 -0
  350. package/lib/templates/content/docs/xstate.md +23 -0
  351. package/lib/types.d.ts +37 -0
  352. package/lib/types.d.ts.map +1 -0
  353. package/lib/utils/docsNavigation.d.ts +9 -0
  354. package/lib/utils/docsNavigation.d.ts.map +1 -0
  355. package/lib/utils/docsNavigation.js +37 -0
  356. package/lib/utils/docsNavigation.js.map +1 -0
  357. package/lib/utils/helpCenterUtils.d.ts +26 -0
  358. package/lib/utils/helpCenterUtils.d.ts.map +1 -0
  359. package/lib/utils/index.d.ts +3 -0
  360. package/lib/utils/index.d.ts.map +1 -0
  361. package/lib/utils/index.js +3 -0
  362. package/lib/utils/index.js.map +1 -0
  363. package/lib/utils/markdownLoader.d.ts +36 -0
  364. package/lib/utils/markdownLoader.d.ts.map +1 -0
  365. package/lib/utils/markdownLoader.js +2242 -0
  366. package/lib/utils/markdownLoader.js.map +1 -0
  367. package/package.json +71 -0
@@ -0,0 +1,1395 @@
1
+ # ๐ŸŽจ Frontend: Organization-Scoped Form Creation Guide
2
+
3
+ ## Overview
4
+
5
+ This guide covers creating React forms that work with organization-scoped entities in the AdminIDE stack. The pattern ensures proper organization context is maintained throughout the form lifecycle, from data fetching to mutations.
6
+
7
+ **Use this guide when creating forms for entities that belong to an organization (Tags, Projects, Vaults, etc.)**
8
+
9
+ ## ๐Ÿ”‘ Core Principles
10
+
11
+ 1. **Organization from Route Parameters**: Extract `orgName` from URL params
12
+ 2. **GraphQL Variables**: Pass `orgName` to all queries requiring organization context
13
+ 3. **State Management**: Use XState machines for complex form workflows
14
+ 4. **Type Safety**: Leverage generated GraphQL types from `common/graphql`
15
+ 5. **Error Handling**: Handle partial data gracefully with error policies
16
+
17
+ ---
18
+
19
+ ## ๐Ÿ“‹ Complete Frontend Implementation Pattern
20
+
21
+ ### **Step 1: React Component Structure**
22
+
23
+ ```tsx
24
+ // TagsView.tsx - Organization-scoped entity management
25
+ import React, { useState } from 'react';
26
+ import { useMachine } from '@xstate/react';
27
+ import { fromPromise } from 'xstate';
28
+ import { useLoaderData, useParams } from '@remix-run/react';
29
+ import {
30
+ useCreateTagMutation,
31
+ useRemoveTagMutation,
32
+ useGetTagsQuery,
33
+ useUpdateTagMutation,
34
+ useGetTagTypesQuery,
35
+ useGetProjectsQuery,
36
+ } from 'common/graphql';
37
+ import { tagsMachine, Tag } from '../tagsMachine';
38
+
39
+ export default function TagsView() {
40
+ // ๐Ÿ”‘ CRITICAL: Extract organization from route params
41
+ const { orgName } = useParams<{ orgName: string }>();
42
+ const loaderData: any = useLoaderData();
43
+ const userId = loaderData.dataContext.userId;
44
+
45
+ // ๐Ÿ”‘ CRITICAL: GraphQL Queries with orgName variable
46
+ const {
47
+ data: tagsData,
48
+ refetch: tagsRefetch,
49
+ loading: tagsLoading,
50
+ error: tagsError,
51
+ } = useGetTagsQuery({
52
+ variables: { orgName }, // Pass organization context
53
+ errorPolicy: 'all', // Allow partial data
54
+ });
55
+
56
+ const { data: projectsData, error: projectsError } = useGetProjectsQuery({
57
+ variables: { orgName }, // Organization context required
58
+ fetchPolicy: 'cache-only',
59
+ errorPolicy: 'all',
60
+ });
61
+
62
+ const { data: tagTypesData, error: tagTypesError } = useGetTagTypesQuery({
63
+ errorPolicy: 'all',
64
+ });
65
+
66
+ // ๐Ÿ”‘ GraphQL Mutations
67
+ const [createTag] = useCreateTagMutation();
68
+ const [updateTag] = useUpdateTagMutation();
69
+ const [removeTag] = useRemoveTagMutation();
70
+
71
+ // Toast notifications for UX feedback
72
+ const [toast, setToast] = useState({ show: false, message: '', type: '' });
73
+
74
+ // ๐Ÿ”‘ XState Machine with actor implementations
75
+ const [state, send] = useMachine(
76
+ tagsMachine.provide({
77
+ actors: {
78
+ // Fetch actor - loads tags from query data
79
+ fetchTags: fromPromise(async () => {
80
+ const tags = tagsData?.getTags?.data || [];
81
+ // Filter out null/invalid entries
82
+ return tags.filter((tag) => tag && tag.id);
83
+ }),
84
+
85
+ // Fetch related data
86
+ fetchProjects: fromPromise(async () => {
87
+ const projects = projectsData?.getProjects?.data || [];
88
+ return projects.filter((project) => project && project.id);
89
+ }),
90
+
91
+ fetchTagTypes: fromPromise(async () => {
92
+ const tagTypes = tagTypesData?.getTagTypes || [];
93
+ return tagTypes.filter((tagType) => tagType && tagType.id);
94
+ }),
95
+
96
+ // Submit actor - creates new tag
97
+ submitTag: fromPromise(async ({ input }) => {
98
+ const { tag } = input;
99
+
100
+ // Client-side validation
101
+ if (!tag.project?.id || !tag.tagType?.id) {
102
+ setToast({
103
+ show: true,
104
+ message: 'Please select both a project and a tag type',
105
+ type: 'error',
106
+ });
107
+ setTimeout(() => setToast({ show: false, message: '', type: '' }), 3000);
108
+ throw new Error('Project and tag type must be selected.');
109
+ }
110
+
111
+ // ๐Ÿ”‘ CRITICAL: Create mutation
112
+ // Note: organization context comes from backend @addAccountContext
113
+ // No need to pass orgName/orgId in mutation variables
114
+ const response = await createTag({
115
+ variables: {
116
+ request: {
117
+ projectId: tag.project.id,
118
+ tagTypeId: tag.tagType.id,
119
+ name: tag.name,
120
+ description: tag.description || '',
121
+ createdBy: userId,
122
+ },
123
+ },
124
+ update: (cache, { data, errors }) => {
125
+ if (data) {
126
+ setToast({
127
+ show: true,
128
+ message: 'Tag has been created!',
129
+ type: 'success',
130
+ });
131
+ setTimeout(() => setToast({ show: false, message: '', type: '' }), 3000);
132
+ } else if (errors) {
133
+ setToast({
134
+ show: true,
135
+ message: `Failed to create tag: ${errors[0]?.message}`,
136
+ type: 'error',
137
+ });
138
+ setTimeout(() => setToast({ show: false, message: '', type: '' }), 4000);
139
+ throw new Error(errors[0].message);
140
+ }
141
+ },
142
+ });
143
+
144
+ // Refresh the list
145
+ tagsRefetch();
146
+ return response.data?.createTag;
147
+ }),
148
+
149
+ // Update actor
150
+ updateTag: fromPromise(async ({ input }) => {
151
+ const { tag } = input;
152
+ const response = await updateTag({
153
+ variables: {
154
+ id: tag.id,
155
+ request: {
156
+ name: tag.name,
157
+ description: tag.description,
158
+ projectId: tag.project?.id,
159
+ tagTypeId: tag.tagType?.id,
160
+ },
161
+ },
162
+ });
163
+ tagsRefetch();
164
+ return response.data?.updateTag;
165
+ }),
166
+
167
+ // Delete actor
168
+ deleteTag: fromPromise(async ({ input }) => {
169
+ const { id } = input;
170
+ await removeTag({
171
+ variables: { id },
172
+ update: (cache, { data, errors }) => {
173
+ if (data) {
174
+ setToast({
175
+ show: true,
176
+ message: 'Tag has been deleted!',
177
+ type: 'success',
178
+ });
179
+ setTimeout(() => setToast({ show: false, message: '', type: '' }), 3000);
180
+ }
181
+ },
182
+ });
183
+ return { id };
184
+ }),
185
+ },
186
+ }),
187
+ );
188
+
189
+ const { filteredTags, projects, tagTypes, searchQuery, isLoading } = state.context;
190
+
191
+ // Trigger data fetch when GraphQL data is available
192
+ React.useEffect(() => {
193
+ if (tagsData && projectsData && tagTypesData) {
194
+ send({ type: 'FETCH' });
195
+ }
196
+ }, [send, tagsData, projectsData, tagTypesData]);
197
+
198
+ // Component render
199
+ return (
200
+ <div className="min-h-screen bg-background">
201
+ {/* Toast notifications */}
202
+ {toast.show && (
203
+ <div
204
+ className={`fixed top-4 right-4 px-4 py-2 rounded-themed shadow-themed-lg z-50
205
+ ${
206
+ toast.type === 'success'
207
+ ? 'bg-success text-success-foreground'
208
+ : 'bg-destructive text-destructive-foreground'
209
+ }`}
210
+ >
211
+ <span>{toast.message}</span>
212
+ </div>
213
+ )}
214
+
215
+ {/* Header */}
216
+ <div className="flex justify-between items-center mb-6">
217
+ <h1 className="text-2xl font-bold text-foreground">Tags</h1>
218
+ <button onClick={() => send({ type: 'OPEN_CREATE_MODAL' })} className="themed-button focus-themed">
219
+ Create Tag
220
+ </button>
221
+ </div>
222
+
223
+ {/* Search */}
224
+ <input
225
+ type="text"
226
+ placeholder="Search tags..."
227
+ value={searchQuery}
228
+ onChange={(e) => send({ type: 'SEARCH', query: e.target.value })}
229
+ className="themed-input w-full mb-4 focus-themed"
230
+ />
231
+
232
+ {/* Tags list */}
233
+ {isLoading ? (
234
+ <div>Loading...</div>
235
+ ) : (
236
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
237
+ {filteredTags.map((tag) => (
238
+ <div key={tag.id} className="themed-card p-4">
239
+ <h3 className="font-semibold">{tag.name}</h3>
240
+ <p className="text-sm text-muted-foreground">{tag.description}</p>
241
+ </div>
242
+ ))}
243
+ </div>
244
+ )}
245
+
246
+ {/* Modal Form */}
247
+ <TagModal
248
+ isOpen={state.context.isCreateModalOpen || state.context.isEditModalOpen}
249
+ onClose={() => send({ type: 'CLOSE_CREATE_MODAL' })}
250
+ title={state.context.isEditModalOpen ? 'Edit Tag' : 'Create Tag'}
251
+ draftTag={state.context.draftTag}
252
+ projects={projects}
253
+ tagTypes={tagTypes}
254
+ send={send}
255
+ onSubmit={(e) => {
256
+ e.preventDefault();
257
+ if (state.context.isEditModalOpen) {
258
+ send({ type: 'UPDATE_TAG' });
259
+ } else {
260
+ send({ type: 'SUBMIT_TAG' });
261
+ }
262
+ }}
263
+ />
264
+ </div>
265
+ );
266
+ }
267
+ ```
268
+
269
+ ---
270
+
271
+ ### **Step 2: XState Machine for Form State Management**
272
+
273
+ ```typescript
274
+ // tagsMachine.ts - State machine with organization context
275
+ import { createMachine, assign } from 'xstate';
276
+ import type { ITagType } from 'common/server';
277
+
278
+ // ๐Ÿ”‘ Type definition with organization field
279
+ export type Tag = {
280
+ id: string;
281
+ tagId: string;
282
+ name: string;
283
+ description?: string;
284
+ createdAt: string;
285
+ updatedAt: string;
286
+ createdBy: string;
287
+ orgName: string; // Organization context
288
+ tagType: {
289
+ id: string;
290
+ name: string;
291
+ color: string;
292
+ };
293
+ color: string;
294
+ project: {
295
+ id: string;
296
+ name: string;
297
+ };
298
+ organization: {
299
+ id: string;
300
+ name: string;
301
+ };
302
+ };
303
+
304
+ type TagsContext = {
305
+ tags: Tag[];
306
+ projects: Project[];
307
+ tagTypes: ITagType[];
308
+ filteredTags: Tag[];
309
+ searchQuery: string;
310
+ isLoading: boolean;
311
+ error: string | null;
312
+ isCreateModalOpen: boolean;
313
+ isEditModalOpen: boolean;
314
+ draftTag: Partial<Tag> | null;
315
+ validationErrors?: Record<string, string>;
316
+ tagsLoaded: boolean;
317
+ projectsLoaded: boolean;
318
+ tagTypesLoaded: boolean;
319
+ };
320
+
321
+ type TagsEvent =
322
+ | { type: 'FETCH' }
323
+ | { type: 'SEARCH'; query: string }
324
+ | { type: 'OPEN_CREATE_MODAL' }
325
+ | { type: 'CLOSE_CREATE_MODAL' }
326
+ | { type: 'OPEN_EDIT_MODAL'; tag: Tag }
327
+ | { type: 'UPDATE_DRAFT'; value: Partial<Tag> }
328
+ | { type: 'SUBMIT_TAG' }
329
+ | { type: 'UPDATE_TAG' }
330
+ | { type: 'DELETE_TAG'; id: string };
331
+
332
+ const defaultDraftTag: Partial<Tag> = {
333
+ name: '',
334
+ description: '',
335
+ };
336
+
337
+ // Helper function for search filtering
338
+ const filterTags = (tags: Tag[], query: string): Tag[] => {
339
+ if (!query) return tags;
340
+ const lowercaseQuery = query.toLowerCase();
341
+ return tags.filter(
342
+ (tag) =>
343
+ tag.name.toLowerCase().includes(lowercaseQuery) ||
344
+ (tag.description?.toLowerCase() || '').includes(lowercaseQuery),
345
+ );
346
+ };
347
+
348
+ export const tagsMachine = createMachine({
349
+ id: 'tags',
350
+ types: {} as {
351
+ context: TagsContext;
352
+ events: TagsEvent;
353
+ },
354
+ initial: 'loading',
355
+ context: {
356
+ tags: [],
357
+ projects: [],
358
+ tagTypes: [],
359
+ filteredTags: [],
360
+ searchQuery: '',
361
+ isLoading: true,
362
+ error: null,
363
+ isCreateModalOpen: false,
364
+ isEditModalOpen: false,
365
+ draftTag: null,
366
+ validationErrors: {},
367
+ tagsLoaded: false,
368
+ projectsLoaded: false,
369
+ tagTypesLoaded: false,
370
+ },
371
+ states: {
372
+ loading: {
373
+ // Parallel invocations for fetching multiple data sources
374
+ invoke: [
375
+ {
376
+ src: 'fetchTags',
377
+ onDone: {
378
+ actions: assign({
379
+ tags: ({ event }) => event.output,
380
+ filteredTags: ({ event }) => event.output,
381
+ tagsLoaded: true,
382
+ }),
383
+ },
384
+ onError: {
385
+ target: 'error',
386
+ actions: assign({
387
+ error: ({ event }) => String(event.error),
388
+ isLoading: false,
389
+ }),
390
+ },
391
+ },
392
+ {
393
+ src: 'fetchProjects',
394
+ onDone: {
395
+ actions: assign({
396
+ projects: ({ event }) => event.output,
397
+ projectsLoaded: true,
398
+ }),
399
+ },
400
+ },
401
+ {
402
+ src: 'fetchTagTypes',
403
+ onDone: {
404
+ actions: assign({
405
+ tagTypes: ({ event }) => event.output,
406
+ tagTypesLoaded: true,
407
+ }),
408
+ },
409
+ },
410
+ ],
411
+ // Transition to idle when all data is loaded
412
+ always: {
413
+ target: 'idle',
414
+ guard: ({ context }) => context.tagsLoaded && context.projectsLoaded && context.tagTypesLoaded,
415
+ actions: assign({ isLoading: false }),
416
+ },
417
+ },
418
+ idle: {
419
+ on: {
420
+ FETCH: {
421
+ target: 'loading',
422
+ actions: assign({
423
+ isLoading: true,
424
+ tagsLoaded: false,
425
+ projectsLoaded: false,
426
+ tagTypesLoaded: false,
427
+ }),
428
+ },
429
+ SEARCH: {
430
+ actions: assign({
431
+ searchQuery: ({ event }) => event.query || '',
432
+ filteredTags: ({ context, event }) => filterTags(context.tags, event.query),
433
+ }),
434
+ },
435
+ OPEN_CREATE_MODAL: {
436
+ actions: assign({
437
+ isCreateModalOpen: true,
438
+ draftTag: () => ({ ...defaultDraftTag }),
439
+ }),
440
+ },
441
+ CLOSE_CREATE_MODAL: {
442
+ actions: assign({
443
+ isCreateModalOpen: false,
444
+ isEditModalOpen: false,
445
+ draftTag: null,
446
+ }),
447
+ },
448
+ OPEN_EDIT_MODAL: {
449
+ actions: assign({
450
+ isEditModalOpen: true,
451
+ draftTag: ({ event }) => ({ ...event.tag }),
452
+ }),
453
+ },
454
+ UPDATE_DRAFT: {
455
+ actions: assign({
456
+ draftTag: ({ context, event }) => ({
457
+ ...context.draftTag,
458
+ ...event.value,
459
+ }),
460
+ }),
461
+ },
462
+ SUBMIT_TAG: {
463
+ target: 'submitting',
464
+ guard: ({ context }) => Boolean(context.draftTag?.name),
465
+ },
466
+ UPDATE_TAG: {
467
+ target: 'updating',
468
+ },
469
+ DELETE_TAG: {
470
+ target: 'deleting',
471
+ },
472
+ },
473
+ },
474
+ submitting: {
475
+ invoke: {
476
+ src: 'submitTag',
477
+ input: ({ context }) => ({ tag: context.draftTag! }),
478
+ onDone: {
479
+ target: 'idle',
480
+ actions: assign({
481
+ tags: ({ context, event }) => [...context.tags, event.output],
482
+ filteredTags: ({ context, event }) => {
483
+ const updatedTags = [...context.tags, event.output];
484
+ return filterTags(updatedTags, context.searchQuery);
485
+ },
486
+ isCreateModalOpen: false,
487
+ draftTag: null,
488
+ }),
489
+ },
490
+ onError: {
491
+ target: 'idle',
492
+ actions: assign({
493
+ error: ({ event }) => String(event.error),
494
+ }),
495
+ },
496
+ },
497
+ },
498
+ updating: {
499
+ invoke: {
500
+ src: 'updateTag',
501
+ input: ({ context }) => ({ tag: context.draftTag! }),
502
+ onDone: {
503
+ target: 'idle',
504
+ actions: assign({
505
+ tags: ({ context, event }) =>
506
+ context.tags.map((tag) => (tag.id === event.output.id ? event.output : tag)),
507
+ filteredTags: ({ context, event }) => {
508
+ const updatedTags = context.tags.map((tag) =>
509
+ tag.id === event.output.id ? event.output : tag,
510
+ );
511
+ return filterTags(updatedTags, context.searchQuery);
512
+ },
513
+ isEditModalOpen: false,
514
+ draftTag: null,
515
+ }),
516
+ },
517
+ onError: {
518
+ target: 'idle',
519
+ actions: assign({
520
+ error: ({ event }) => String(event.error),
521
+ }),
522
+ },
523
+ },
524
+ },
525
+ deleting: {
526
+ invoke: {
527
+ src: 'deleteTag',
528
+ input: ({ event }) => ({ id: (event as any).id }),
529
+ onDone: {
530
+ target: 'idle',
531
+ actions: assign({
532
+ tags: ({ context, event }) => context.tags.filter((tag) => tag.id !== event.output.id),
533
+ filteredTags: ({ context, event }) => {
534
+ const updatedTags = context.tags.filter((tag) => tag.id !== event.output.id);
535
+ return filterTags(updatedTags, context.searchQuery);
536
+ },
537
+ }),
538
+ },
539
+ },
540
+ },
541
+ error: {
542
+ on: {
543
+ FETCH: 'loading',
544
+ },
545
+ },
546
+ },
547
+ });
548
+ ```
549
+
550
+ ---
551
+
552
+ ### **Step 3: Modal Form Component**
553
+
554
+ ```tsx
555
+ // TagModal.tsx - Reusable form modal component
556
+ import React from 'react';
557
+ import type { ITagType } from 'common/server';
558
+
559
+ interface TagModalProps {
560
+ isOpen: boolean;
561
+ onClose: () => void;
562
+ title: string;
563
+ draftTag: any;
564
+ projects: any[];
565
+ tagTypes: ITagType[];
566
+ send: any;
567
+ onSubmit: (e: React.FormEvent) => void;
568
+ }
569
+
570
+ // ๐Ÿ”‘ Extract modal outside parent to prevent re-renders
571
+ const TagModal: React.FC<TagModalProps> = ({
572
+ isOpen,
573
+ onClose,
574
+ title,
575
+ draftTag,
576
+ projects,
577
+ tagTypes,
578
+ send,
579
+ onSubmit,
580
+ }) => {
581
+ if (!isOpen || !draftTag) return null;
582
+
583
+ return (
584
+ <div className="fixed inset-0 z-10 overflow-y-auto">
585
+ <div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
586
+ {/* Backdrop */}
587
+ <div
588
+ className="fixed inset-0 bg-black/50 backdrop-blur-sm transition-opacity"
589
+ onClick={onClose}
590
+ aria-hidden="true"
591
+ />
592
+
593
+ {/* Center modal */}
594
+ <span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
595
+ &#8203;
596
+ </span>
597
+
598
+ {/* Modal panel */}
599
+ <div className="themed-card inline-block align-bottom rounded-themed-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-themed-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6">
600
+ <div className="sm:flex sm:items-start">
601
+ <div className="mt-3 text-center sm:mt-0 sm:text-left w-full">
602
+ <h3 className="text-lg leading-6 font-medium text-foreground">{title}</h3>
603
+
604
+ <form onSubmit={onSubmit} className="mt-6 space-y-4">
605
+ {/* Name Field */}
606
+ <div>
607
+ <label htmlFor="name" className="block text-sm font-medium text-foreground">
608
+ Name <span className="text-destructive">*</span>
609
+ </label>
610
+ <input
611
+ type="text"
612
+ id="name"
613
+ value={draftTag.name || ''}
614
+ onChange={(e) =>
615
+ send({
616
+ type: 'UPDATE_DRAFT',
617
+ value: { name: e.target.value },
618
+ })
619
+ }
620
+ className="themed-input mt-1 block w-full rounded-themed focus-themed"
621
+ required
622
+ autoFocus
623
+ aria-required="true"
624
+ />
625
+ </div>
626
+
627
+ {/* Project Selection */}
628
+ <div>
629
+ <label htmlFor="project" className="block text-sm font-medium text-foreground">
630
+ Project <span className="text-destructive">*</span>
631
+ </label>
632
+ <select
633
+ id="project"
634
+ value={draftTag.project?.id || ''}
635
+ required
636
+ onChange={(e) => {
637
+ const selectedProject = projects.find((p) => p.id === e.target.value);
638
+ send({
639
+ type: 'UPDATE_DRAFT',
640
+ value: {
641
+ project: selectedProject
642
+ ? {
643
+ id: selectedProject.id,
644
+ name: selectedProject.name,
645
+ }
646
+ : null,
647
+ },
648
+ });
649
+ }}
650
+ className="themed-input mt-1 block w-full rounded-themed focus-themed"
651
+ aria-required="true"
652
+ >
653
+ <option value="">Select a project</option>
654
+ {projects.map((project) => (
655
+ <option key={project.id} value={project.id}>
656
+ {project.name}
657
+ </option>
658
+ ))}
659
+ </select>
660
+ </div>
661
+
662
+ {/* Tag Type Selection */}
663
+ <div>
664
+ <label htmlFor="tagType" className="block text-sm font-medium text-foreground">
665
+ Tag Type <span className="text-destructive">*</span>
666
+ </label>
667
+ <select
668
+ id="tagType"
669
+ value={draftTag.tagType?.id || ''}
670
+ onChange={(e) => {
671
+ const selectedTagType = tagTypes.find((tt) => tt.id === e.target.value);
672
+ send({
673
+ type: 'UPDATE_DRAFT',
674
+ value: {
675
+ tagType: selectedTagType
676
+ ? {
677
+ id: selectedTagType.id,
678
+ name: selectedTagType.name,
679
+ color: selectedTagType.color,
680
+ }
681
+ : null,
682
+ },
683
+ });
684
+ }}
685
+ className="themed-input mt-1 block w-full rounded-themed focus-themed"
686
+ required
687
+ aria-required="true"
688
+ >
689
+ <option value="">Select a tag type</option>
690
+ {tagTypes.map((tagType) => (
691
+ <option key={tagType.id} value={tagType.id}>
692
+ {tagType.name}
693
+ </option>
694
+ ))}
695
+ </select>
696
+ </div>
697
+
698
+ {/* Description Field */}
699
+ <div>
700
+ <label htmlFor="description" className="block text-sm font-medium text-foreground">
701
+ Description <span className="text-muted-foreground">(Optional)</span>
702
+ </label>
703
+ <textarea
704
+ id="description"
705
+ value={draftTag.description || ''}
706
+ onChange={(e) =>
707
+ send({
708
+ type: 'UPDATE_DRAFT',
709
+ value: { description: e.target.value },
710
+ })
711
+ }
712
+ rows={3}
713
+ className="themed-input mt-1 block w-full rounded-themed focus-themed"
714
+ placeholder="Enter a description for this tag..."
715
+ />
716
+ </div>
717
+
718
+ {/* Action Buttons */}
719
+ <div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
720
+ <button
721
+ type="submit"
722
+ className="themed-button w-full sm:ml-3 sm:w-auto focus-themed"
723
+ >
724
+ {draftTag.id ? 'Update Tag' : 'Create Tag'}
725
+ </button>
726
+ <button
727
+ type="button"
728
+ onClick={onClose}
729
+ className="bg-secondary text-secondary-foreground w-full sm:w-auto mt-3 sm:mt-0 px-4 py-2 rounded-themed hover:bg-secondary/80 transition-colors focus-themed"
730
+ >
731
+ Cancel
732
+ </button>
733
+ </div>
734
+ </form>
735
+ </div>
736
+ </div>
737
+ </div>
738
+ </div>
739
+ </div>
740
+ );
741
+ };
742
+
743
+ export default TagModal;
744
+ ```
745
+
746
+ ---
747
+
748
+ ### **Step 4: GraphQL Query Patterns**
749
+
750
+ ```typescript
751
+ // Queries with organization context
752
+ const { data, loading, error } = useGetTagsQuery({
753
+ variables: { orgName }, // โœ… Pass organization context
754
+ errorPolicy: 'all', // โœ… Handle partial data gracefully
755
+ });
756
+
757
+ // Mutations - organization context from backend @addAccountContext
758
+ const [createTag] = useCreateTagMutation();
759
+ const response = await createTag({
760
+ variables: {
761
+ request: {
762
+ projectId: tag.project.id,
763
+ tagTypeId: tag.tagType.id,
764
+ name: tag.name,
765
+ description: tag.description,
766
+ },
767
+ // โœ… NO orgName/orgId needed - backend gets it from userContext
768
+ },
769
+ // โœ… Cache update after mutation
770
+ update: (cache, { data, errors }) => {
771
+ if (data?.createTag) {
772
+ // Optimistically update cache
773
+ cache.modify({
774
+ fields: {
775
+ getTags(existingTags = { data: [] }) {
776
+ return {
777
+ ...existingTags,
778
+ data: [...existingTags.data, data.createTag],
779
+ };
780
+ },
781
+ },
782
+ });
783
+ }
784
+ },
785
+ });
786
+ ```
787
+
788
+ ---
789
+
790
+ ## ๐ŸŽฏ Frontend Implementation Checklist
791
+
792
+ ### **React Component Setup**
793
+
794
+ - [ ] Extract `orgName` from `useParams<{ orgName: string }>()`
795
+ - [ ] Pass `orgName` to all GraphQL queries requiring organization context
796
+ - [ ] Use `errorPolicy: 'all'` for partial data handling
797
+ - [ ] Implement loading and error states
798
+ - [ ] Add toast notifications for user feedback
799
+ - [ ] Import hooks from `common/graphql` (not local files)
800
+
801
+ ### **XState Machine**
802
+
803
+ - [ ] Define comprehensive context type with all form fields
804
+ - [ ] Include organization-related fields in entity types
805
+ - [ ] Create states: `loading`, `idle`, `submitting`, `updating`, `deleting`, `error`
806
+ - [ ] Implement proper state transitions with guards
807
+ - [ ] Use `fromPromise` for async operations (fetch/submit/update/delete actors)
808
+ - [ ] Handle validation in guards or actors
809
+ - [ ] Use `assign` for context updates
810
+ - [ ] Implement search/filter logic in event handlers
811
+
812
+ ### **Form Modal**
813
+
814
+ - [ ] Extract modal component outside parent to prevent re-renders
815
+ - [ ] Implement controlled inputs with XState `send` function
816
+ - [ ] Add proper validation with error messages
817
+ - [ ] Use `send({ type: 'UPDATE_DRAFT', value })` for field updates
818
+ - [ ] Handle form submission with `send({ type: 'SUBMIT_TAG' })`
819
+ - [ ] Add cancel/close functionality
820
+ - [ ] Add accessibility attributes (`aria-required`, `aria-label`)
821
+ - [ ] Auto-focus first input field
822
+ - [ ] Show loading state on submit button
823
+
824
+ ### **GraphQL Integration**
825
+
826
+ - [ ] Import generated hooks from `common/graphql`
827
+ - [ ] Pass organization variables (`orgName`) to queries
828
+ - [ ] Handle mutation responses with optimistic updates
829
+ - [ ] Implement cache updates after mutations using `update` callback
830
+ - [ ] Refetch queries after successful mutations if needed
831
+ - [ ] Use `errorPolicy: 'all'` to handle partial data
832
+ - [ ] Filter out null/invalid entries from response data
833
+
834
+ ### **Error Handling**
835
+
836
+ - [ ] Use `errorPolicy: 'all'` for queries
837
+ - [ ] Filter null/invalid data entries with `.filter(item => item && item.id)`
838
+ - [ ] Display partial data warnings to users
839
+ - [ ] Show user-friendly error messages in toast notifications
840
+ - [ ] Log detailed errors to console for debugging
841
+ - [ ] Handle network errors gracefully
842
+ - [ ] Validate required fields before submission
843
+
844
+ ### **User Experience**
845
+
846
+ - [ ] Add loading spinners during data fetch
847
+ - [ ] Show toast notifications for success/error/info
848
+ - [ ] Auto-hide toasts after 3-4 seconds
849
+ - [ ] Disable system/default entities from editing/deletion
850
+ - [ ] Validate required fields before submission
851
+ - [ ] Show validation errors inline
852
+ - [ ] Add empty state messages when no data
853
+ - [ ] Implement search/filter functionality
854
+ - [ ] Add keyboard shortcuts (Escape to close modal)
855
+ - [ ] Make modals responsive for mobile
856
+
857
+ ---
858
+
859
+ ## ๐Ÿšจ Common Frontend Mistakes
860
+
861
+ ### โŒ Mistake 1: Missing Organization Context in Queries
862
+
863
+ ```tsx
864
+ // โŒ WRONG - No organization context
865
+ const { data } = useGetTagsQuery();
866
+
867
+ // Result: Query returns data for wrong organization or fails
868
+ ```
869
+
870
+ ```tsx
871
+ // โœ… CORRECT - Organization from route params
872
+ const { orgName } = useParams<{ orgName: string }>();
873
+ const { data } = useGetTagsQuery({
874
+ variables: { orgName },
875
+ });
876
+ ```
877
+
878
+ **Why**: Backend uses `@addAccountContext` directive which requires `orgName` to resolve organization context.
879
+
880
+ ---
881
+
882
+ ### โŒ Mistake 2: Not Handling Partial Data
883
+
884
+ ```tsx
885
+ // โŒ WRONG - Will crash on partial errors
886
+ const tags = data.getTags.data;
887
+ tags.map((tag) => <div>{tag.name}</div>);
888
+
889
+ // Result: TypeError: Cannot read property 'data' of undefined
890
+ ```
891
+
892
+ ```tsx
893
+ // โœ… CORRECT - Filter null entries and handle errors
894
+ const tags = (data?.getTags?.data || []).filter((tag) => tag && tag.id);
895
+ tags.map((tag) => <div>{tag.name}</div>);
896
+ ```
897
+
898
+ **Why**: GraphQL can return partial data when some fields fail. Always use optional chaining and filter invalid entries.
899
+
900
+ ---
901
+
902
+ ### โŒ Mistake 3: Creating Modal Inside Parent Component
903
+
904
+ ```tsx
905
+ // โŒ WRONG - Modal recreated on every render
906
+ export function TagsView() {
907
+ const TagModal = () => {
908
+ return <div>Modal content</div>;
909
+ };
910
+
911
+ return (
912
+ <div>
913
+ <TagModal />
914
+ </div>
915
+ );
916
+ }
917
+
918
+ // Result: Performance issues, state loss on re-render
919
+ ```
920
+
921
+ ```tsx
922
+ // โœ… CORRECT - Modal outside parent component
923
+ const TagModal = (props) => {
924
+ return <div>Modal content</div>;
925
+ };
926
+
927
+ export function TagsView() {
928
+ return (
929
+ <div>
930
+ <TagModal {...props} />
931
+ </div>
932
+ );
933
+ }
934
+ ```
935
+
936
+ **Why**: Defining components inside other components causes them to remount on every render, losing internal state.
937
+
938
+ ---
939
+
940
+ ### โŒ Mistake 4: Not Refetching After Mutations
941
+
942
+ ```tsx
943
+ // โŒ WRONG - Stale data after mutation
944
+ await createTag({ variables: { request } });
945
+ // List not updated!
946
+
947
+ // Result: User doesn't see new item until page refresh
948
+ ```
949
+
950
+ ```tsx
951
+ // โœ… CORRECT - Refetch or update cache
952
+ await createTag({
953
+ variables: { request },
954
+ update: (cache, { data }) => {
955
+ // Update Apollo cache
956
+ cache.modify({
957
+ fields: {
958
+ getTags(existing) {
959
+ return {
960
+ ...existing,
961
+ data: [...existing.data, data.createTag],
962
+ };
963
+ },
964
+ },
965
+ });
966
+ },
967
+ });
968
+
969
+ // OR simply refetch
970
+ tagsRefetch();
971
+ ```
972
+
973
+ **Why**: Apollo cache doesn't automatically update after mutations. Must manually update or refetch.
974
+
975
+ ---
976
+
977
+ ### โŒ Mistake 5: Missing Error Policy
978
+
979
+ ```tsx
980
+ // โŒ WRONG - Entire query fails on any error
981
+ const { data } = useGetTagsQuery({
982
+ variables: { orgName },
983
+ });
984
+
985
+ // Result: No data shown even if some tags loaded successfully
986
+ ```
987
+
988
+ ```tsx
989
+ // โœ… CORRECT - Allow partial data with errors
990
+ const { data, error } = useGetTagsQuery({
991
+ variables: { orgName },
992
+ errorPolicy: 'all', // Return partial data even with errors
993
+ });
994
+
995
+ if (error) {
996
+ console.error('Partial error:', error);
997
+ // Still render available data
998
+ }
999
+ ```
1000
+
1001
+ **Why**: Some queries may fail partially (e.g., organization field resolution fails for one item). `errorPolicy: 'all'` allows rendering valid data.
1002
+
1003
+ ---
1004
+
1005
+ ### โŒ Mistake 6: Not Passing orgId in Mutations
1006
+
1007
+ ```tsx
1008
+ // โŒ WRONG - Passing orgId/orgName in mutation
1009
+ await createTag({
1010
+ variables: {
1011
+ request: {
1012
+ organizationId: orgId, // โŒ Not needed!
1013
+ name: tag.name,
1014
+ },
1015
+ },
1016
+ });
1017
+
1018
+ // Result: Backend ignores this - gets orgId from userContext anyway
1019
+ ```
1020
+
1021
+ ```tsx
1022
+ // โœ… CORRECT - No organization in mutation variables
1023
+ await createTag({
1024
+ variables: {
1025
+ request: {
1026
+ // โœ… Backend gets orgId from @addAccountContext directive
1027
+ name: tag.name,
1028
+ projectId: tag.project.id,
1029
+ },
1030
+ },
1031
+ });
1032
+ ```
1033
+
1034
+ **Why**: Backend `@addAccountContext` directive automatically injects organization context into `userContext`. Service layer uses `userContext.orgId`.
1035
+
1036
+ ---
1037
+
1038
+ ### โŒ Mistake 7: Not Validating Before Submission
1039
+
1040
+ ```tsx
1041
+ // โŒ WRONG - No validation
1042
+ const submitTag = async () => {
1043
+ await createTag({ variables: { request: draftTag } });
1044
+ };
1045
+
1046
+ // Result: Backend errors, poor UX
1047
+ ```
1048
+
1049
+ ```tsx
1050
+ // โœ… CORRECT - Validate in actor or guard
1051
+ submitTag: fromPromise(async ({ input }) => {
1052
+ const { tag } = input;
1053
+
1054
+ // Client-side validation
1055
+ if (!tag.name?.trim()) {
1056
+ throw new Error('Name is required');
1057
+ }
1058
+
1059
+ if (!tag.project?.id) {
1060
+ throw new Error('Project must be selected');
1061
+ }
1062
+
1063
+ return await createTag({ variables: { request: tag } });
1064
+ }),
1065
+
1066
+ // In machine
1067
+ SUBMIT_TAG: {
1068
+ target: 'submitting',
1069
+ guard: ({ context }) => {
1070
+ // Guard prevents transition if validation fails
1071
+ return Boolean(context.draftTag?.name?.trim());
1072
+ },
1073
+ },
1074
+ ```
1075
+
1076
+ **Why**: Client-side validation provides immediate feedback and prevents unnecessary network requests.
1077
+
1078
+ ---
1079
+
1080
+ ### โŒ Mistake 8: Importing from Local Instead of common/graphql
1081
+
1082
+ ```tsx
1083
+ // โŒ WRONG - Local import
1084
+ import { useGetTagsQuery } from './queries/generated';
1085
+
1086
+ // Result: May work but defeats code generation purpose
1087
+ ```
1088
+
1089
+ ```tsx
1090
+ // โœ… CORRECT - Import from common/graphql
1091
+ import { useGetTagsQuery } from 'common/graphql';
1092
+ ```
1093
+
1094
+ **Why**: Code generation creates centralized types/hooks in `common/graphql` for consistency across all packages.
1095
+
1096
+ ---
1097
+
1098
+ ## ๐Ÿ“Š Complete Data Flow: Frontend to Backend
1099
+
1100
+ ```
1101
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
1102
+ โ”‚ Frontend โ†’ Backend Flow โ”‚
1103
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
1104
+
1105
+ 1. USER NAVIGATES TO ROUTE
1106
+ URL: /org/acme-corp/projects/tags
1107
+ Route: <Route path="/:orgName/projects/tags" />
1108
+
1109
+ 2. REACT COMPONENT MOUNTS
1110
+ const { orgName } = useParams(); // "acme-corp"
1111
+
1112
+ 3. GRAPHQL QUERY EXECUTED
1113
+ useGetTagsQuery({
1114
+ variables: { orgName: "acme-corp" }
1115
+ })
1116
+ โ†“
1117
+ Apollo Client sends to GraphQL server
1118
+
1119
+ 4. BACKEND RECEIVES QUERY
1120
+ Query getTags(orgName: "acme-corp")
1121
+ โ†“
1122
+ @addAccountContext directive intercepts
1123
+ โ†“
1124
+ Resolves: orgName โ†’ orgId via OrganizationService.getBySlug()
1125
+ โ†“
1126
+ Injects userContext.orgId = "507f1f77bcf86cd799439011"
1127
+
1128
+ 5. RESOLVER LAYER
1129
+ getTags: (_, args, { tagService, userContext }) =>
1130
+ tagService.getTags(userContext.orgId, args.projectId, ...)
1131
+ โ†“
1132
+ Resolver uses userContext.orgId (NOT args.orgName)
1133
+
1134
+ 6. SERVICE LAYER
1135
+ getTags(orgId: string, projectId?: string, ...) {
1136
+ // orgId already resolved - no slug service needed
1137
+ const match = {
1138
+ organization: new mongoose.Types.ObjectId(orgId),
1139
+ ...(projectId && { project: new mongoose.Types.ObjectId(projectId) })
1140
+ };
1141
+ return tagRepository.getAll({ criteria: match });
1142
+ }
1143
+
1144
+ 7. REPOSITORY LAYER
1145
+ getAll(criteria: { organization: ObjectId }) {
1146
+ return TagModel.find(criteria)
1147
+ .populate('organization')
1148
+ .populate('project')
1149
+ .populate('tagType');
1150
+ }
1151
+
1152
+ 8. DATABASE QUERY
1153
+ db.tags.find({
1154
+ organization: ObjectId("507f1f77bcf86cd799439011")
1155
+ })
1156
+
1157
+ 9. RESPONSE TO FRONTEND
1158
+ {
1159
+ getTags: {
1160
+ totalCount: 5,
1161
+ data: [
1162
+ {
1163
+ id: "tag1",
1164
+ name: "Bug",
1165
+ organization: {
1166
+ id: "507f1f77bcf86cd799439011",
1167
+ name: "ACME Corp"
1168
+ },
1169
+ project: { id: "proj1", name: "Project A" },
1170
+ tagType: { id: "type1", name: "Bug", color: "#ff0000" }
1171
+ },
1172
+ // ... more tags
1173
+ ]
1174
+ }
1175
+ }
1176
+
1177
+ 10. FRONTEND STATE UPDATE
1178
+ XState machine:
1179
+ - fetchTags actor receives response
1180
+ - Filters null entries: .filter(tag => tag && tag.id)
1181
+ - Updates context: assign({ tags: validTags })
1182
+ โ†“
1183
+ React re-renders with new data
1184
+ โ†“
1185
+ UI displays tag list
1186
+
1187
+ 11. USER CREATES TAG
1188
+ Form submission โ†’ send({ type: 'SUBMIT_TAG' })
1189
+ โ†“
1190
+ Machine transitions to 'submitting' state
1191
+ โ†“
1192
+ submitTag actor invoked:
1193
+
1194
+ useCreateTagMutation({
1195
+ variables: {
1196
+ request: {
1197
+ projectId: "proj1",
1198
+ tagTypeId: "type1",
1199
+ name: "New Bug Tag"
1200
+ }
1201
+ }
1202
+ })
1203
+ โ†“
1204
+ Backend @addAccountContext gets orgId from request headers
1205
+ โ†“
1206
+ Service creates tag with organization ObjectId
1207
+ โ†“
1208
+ Response with new tag
1209
+ โ†“
1210
+ Apollo cache updated via 'update' callback
1211
+ โ†“
1212
+ Query refetched: tagsRefetch()
1213
+ โ†“
1214
+ Machine transitions to 'idle'
1215
+ โ†“
1216
+ UI shows success toast
1217
+ โ†“
1218
+ Modal closes
1219
+ ```
1220
+
1221
+ ---
1222
+
1223
+ ## ๐ŸŽจ Styling Best Practices
1224
+
1225
+ ### **Use Themed Classes**
1226
+
1227
+ The AdminIDE stack uses theme-aware CSS classes that adapt to light/dark modes:
1228
+
1229
+ ```tsx
1230
+ // Input fields
1231
+ <input className="themed-input focus-themed" />
1232
+
1233
+ // Buttons
1234
+ <button className="themed-button focus-themed">Submit</button>
1235
+
1236
+ // Cards/Panels
1237
+ <div className="themed-card shadow-themed-lg" />
1238
+
1239
+ // Success/Error states
1240
+ <div className="bg-success text-success-foreground">Success!</div>
1241
+ <div className="bg-destructive text-destructive-foreground">Error!</div>
1242
+
1243
+ // Text colors
1244
+ <p className="text-foreground">Primary text</p>
1245
+ <p className="text-muted-foreground">Secondary text</p>
1246
+
1247
+ // Background colors
1248
+ <div className="bg-background">Main background</div>
1249
+ <div className="bg-card">Card background</div>
1250
+ ```
1251
+
1252
+ ### **Responsive Design**
1253
+
1254
+ ```tsx
1255
+ // Mobile-first approach
1256
+ <div className="flex flex-col sm:flex-row">
1257
+ <button className="w-full sm:w-auto sm:ml-3">Submit</button>
1258
+ </div>
1259
+
1260
+ // Grid layouts
1261
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
1262
+ {items.map(item => <Card key={item.id} />)}
1263
+ </div>
1264
+
1265
+ // Responsive padding
1266
+ <div className="px-4 sm:px-6 lg:px-8">
1267
+ Content
1268
+ </div>
1269
+ ```
1270
+
1271
+ ### **Accessibility**
1272
+
1273
+ ```tsx
1274
+ // Labels and inputs
1275
+ <label htmlFor="name" className="block text-sm font-medium">
1276
+ Name <span className="text-destructive">*</span>
1277
+ </label>
1278
+ <input
1279
+ id="name"
1280
+ aria-required="true"
1281
+ aria-invalid={!!errors.name}
1282
+ aria-describedby={errors.name ? "name-error" : undefined}
1283
+ autoFocus
1284
+ />
1285
+ {errors.name && (
1286
+ <p id="name-error" className="text-destructive text-sm mt-1">
1287
+ {errors.name}
1288
+ </p>
1289
+ )}
1290
+
1291
+ // Buttons
1292
+ <button
1293
+ aria-label="Close modal"
1294
+ onClick={onClose}
1295
+ >
1296
+ <XIcon aria-hidden="true" />
1297
+ </button>
1298
+
1299
+ // Loading states
1300
+ <button disabled={isLoading} aria-busy={isLoading}>
1301
+ {isLoading ? 'Saving...' : 'Save'}
1302
+ </button>
1303
+ ```
1304
+
1305
+ ---
1306
+
1307
+ ## ๐Ÿงช Testing Checklist
1308
+
1309
+ ### **Query Testing**
1310
+
1311
+ - [ ] Test with valid organization context (`orgName` in params)
1312
+ - [ ] Test without organization context (should error or redirect)
1313
+ - [ ] Test partial data scenarios (some items fail to load)
1314
+ - [ ] Test network errors (offline, timeout)
1315
+ - [ ] Test empty results (no tags for organization)
1316
+ - [ ] Test filtering/search with various inputs
1317
+ - [ ] Test loading states displayed correctly
1318
+
1319
+ ### **Mutation Testing**
1320
+
1321
+ - [ ] Test successful creation with valid data
1322
+ - [ ] Test successful update with changed fields
1323
+ - [ ] Test successful deletion
1324
+ - [ ] Test validation errors (missing required fields)
1325
+ - [ ] Test backend errors (duplicate names, constraints)
1326
+ - [ ] Test network errors during submission
1327
+ - [ ] Test cache updates after mutations
1328
+ - [ ] Test refetch after mutations
1329
+
1330
+ ### **State Machine Testing**
1331
+
1332
+ - [ ] Test all state transitions (loading โ†’ idle โ†’ submitting โ†’ idle)
1333
+ - [ ] Test event handlers (FETCH, SEARCH, SUBMIT_TAG, etc.)
1334
+ - [ ] Test guards (SUBMIT_TAG blocked if name empty)
1335
+ - [ ] Test context updates (assign actions work correctly)
1336
+ - [ ] Test error states and recovery
1337
+ - [ ] Test parallel invocations (fetchTags, fetchProjects, fetchTagTypes)
1338
+
1339
+ ### **UI/UX Testing**
1340
+
1341
+ - [ ] Test toast notifications appear and disappear
1342
+ - [ ] Test modal open/close animations
1343
+ - [ ] Test form field updates reflect in state
1344
+ - [ ] Test keyboard navigation (Tab, Enter, Escape)
1345
+ - [ ] Test responsive layout on mobile/tablet/desktop
1346
+ - [ ] Test accessibility with screen reader
1347
+ - [ ] Test dark/light theme switching
1348
+
1349
+ ### **Integration Testing**
1350
+
1351
+ - [ ] Test complete flow: load โ†’ create โ†’ update โ†’ delete
1352
+ - [ ] Test with multiple browser tabs (concurrent edits)
1353
+ - [ ] Test with slow network (loading states)
1354
+ - [ ] Test with backend errors (500, 404, validation)
1355
+ - [ ] Test authentication errors (token expired)
1356
+ - [ ] Test permission errors (unauthorized actions)
1357
+
1358
+ ---
1359
+
1360
+ ## ๐Ÿ“š Key Takeaways
1361
+
1362
+ 1. โœ… **Always extract `orgName` from route params** - Required for organization-scoped queries
1363
+ 2. โœ… **Pass `orgName` to all organization-scoped queries** - Backend needs it for context resolution
1364
+ 3. โœ… **Backend handles organization resolution via `@addAccountContext`** - Don't pass orgId in mutations
1365
+ 4. โœ… **Use XState for complex form workflows** - Better state management than useState
1366
+ 5. โœ… **Handle partial data with `errorPolicy: 'all'`** - Gracefully handle field resolution errors
1367
+ 6. โœ… **Update cache or refetch after mutations** - Keep UI in sync with backend
1368
+ 7. โœ… **Provide user feedback with toast notifications** - Confirm actions and show errors
1369
+ 8. โœ… **Extract modals to prevent unnecessary re-renders** - Define outside parent component
1370
+ 9. โœ… **Validate data before submission** - Better UX and fewer backend errors
1371
+ 10. โœ… **Filter null/invalid entries from GraphQL responses** - Prevent runtime errors
1372
+
1373
+ ---
1374
+
1375
+ ## ๐Ÿ”— Related Documentation
1376
+
1377
+ - [Backend Service Creation Template](./LLM-Service-Creation-Template.md) - Backend patterns
1378
+ - [XState Documentation](https://xstate.js.org/docs/) - State machine patterns
1379
+ - [Apollo Client Documentation](https://www.apollographql.com/docs/react/) - GraphQL client
1380
+ - [Remix Documentation](https://remix.run/docs) - React framework
1381
+ - [TailwindCSS Documentation](https://tailwindcss.com/docs) - Utility-first CSS
1382
+
1383
+ ---
1384
+
1385
+ ## ๐Ÿ“ Example: Complete Tag Form Implementation
1386
+
1387
+ See the following files for a complete working example:
1388
+
1389
+ - `packages/client-organization/app/routes/$orgName.projects.tags.tsx` - Main view component
1390
+ - `packages/client-organization/app/machines/tagsMachine.ts` - XState machine
1391
+ - `packages/common/graphql/src/generated.ts` - Generated GraphQL hooks
1392
+
1393
+ ---
1394
+
1395
+ **This frontend pattern ensures consistent organization context throughout the application and provides a smooth user experience!** ๐Ÿš€