dashboard.mdc 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. ---
  2. description: Dashboard package
  3. globs: packages/dashboard/**/*
  4. alwaysApply: false
  5. ---
  6. The project in the /packages/dashboard dir uses the following stack. Please make sure to use these in any solutions:
  7. - React
  8. - Shadcn UI & tailwind
  9. - TanStack Query for data fetching, i.e. useQuery or useMutation. Not Apollo Client.
  10. - TanStack router for any routing & navigation
  11. ## API Interaction
  12. When creating useQuery or useMutation calls, use the following pattern as a guide:
  13. ```
  14. import { api } from '@/graphql/api.js';
  15. import { graphql } from '@/graphql/graphql.js';
  16. import { useQuery } from '@tanstack/react-query';
  17. const taxCategoriesDocument = graphql(`
  18. query TaxCategories($options: TaxCategoryListOptions) {
  19. taxCategories(options: $options) {
  20. items {
  21. id
  22. name
  23. isDefault
  24. }
  25. }
  26. }
  27. `);
  28. export function MyComponent() {
  29. const { data, isLoading, isPending, status } = useQuery({
  30. queryKey: ['taxCategories'],
  31. staleTime: 1000 * 60 * 5,
  32. queryFn: () =>
  33. api.query(taxCategoriesDocument, {
  34. options: {
  35. take: 100,
  36. },
  37. }),
  38. });
  39. }
  40. ```
  41. When performing mutations, use this example as a guide:
  42. ```tsx
  43. import { graphql } from '@/graphql/graphql.js';
  44. import { api } from '@/graphql/api.js';
  45. import { useMutation } from '@tanstack/react-query';
  46. const createProductOptionsMutation = graphql(`
  47. mutation CreateOptionGroups($input: CreateProductOptionGroupInput!) {
  48. createProductOptionGroup(input: $input) {
  49. id
  50. name
  51. options {
  52. id
  53. code
  54. name
  55. }
  56. }
  57. }
  58. `);
  59. export function MyComponent() {
  60. // Note that explicit typings or generic params should _not_ be needed since the
  61. // type info will flow from the use of the `graphql` function, which uses gql.tada.
  62. const createOptionGroupMutation = useMutation({
  63. // note that variables are not passed at this point of declaration
  64. mutationFn: api.mutate(createProductOptionsMutation),
  65. onSuccess: () => {
  66. // whatever makes sense, e.g. show toast
  67. },
  68. onError: () => {
  69. // whatever makes sense
  70. },
  71. });
  72. // ...
  73. function handleClick() {
  74. // variables are passed here
  75. createOptionGroupMutation.mutate({
  76. input: {
  77. // etc
  78. }
  79. })
  80. }
  81. //...
  82. }
  83. ```
  84. ## Package Layout
  85. The architecture of the dashboard is as follows:
  86. - /src/lib contains shared code that is used across the dashboard.
  87. - /src/app contains the routes and pages of the dashboard app.
  88. In the "app" context, we try as much as possible to use the same patterns across all pages. Most of the app routes
  89. are CRUD list/detail pages.
  90. - A representative list page is [administrators.tsx](mdc:packages/dashboard/src/app/routes/_authenticated/_administrators/administrators.tsx)
  91. - A representative detail page is [administrators_.$id.tsx](mdc:packages/dashboard/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx)
  92. These examples show the common components, hooks and patterns that should be used across the app
  93. **Important** All the files in the /src/app/routes/ dir are interpreted as routes by Tanstack
  94. Router. The exception is any file in a `/components`, `/hooks` dir, or any file ending `.graphql.ts`. So
  95. don't just create new directories and files that do not fit this pattern otherwise Tanstack router
  96. will break.
  97. In the "lib" context, we try to re-use components from the /src/lib/components dir, including preferring the Shadcn components
  98. as much as possible from the /src/lib/components/ui dir.
  99. ## Forms
  100. We have a special wrapper around the Shadcn react-hook-form components to make typical form controls
  101. more concise:
  102. ```
  103. <FormFieldWrapper
  104. control={form.control}
  105. name="facetValueIds"
  106. label={<Trans>Facet values</Trans>}
  107. render={({ field }) => (
  108. <AssignedFacetValues facetValues={entity?.facetValues ?? []} {...field} />
  109. )}
  110. />
  111. ```
  112. Please prefer this form over the raw Shadcn components where possible.
  113. ## Localization
  114. Any labels or user-facing messages should use localization following this example:
  115. ```tsx
  116. import { Trans, useLingui } from '@/lib/trans.js';
  117. export function MyComponent() {
  118. const { i18n } = useLingui();
  119. const message = i18n.t('This is a localized string')
  120. return (
  121. <div>
  122. <Trans>This is a localized string in the markup</Trans>
  123. </div>
  124. )
  125. }
  126. ```
  127. ## Typing React Components
  128. React components should have their props objects set to `Readonly<>`, for instance:
  129. ```ts
  130. export function PaymentDetails({ payment, currencyCode }: Readonly<PaymentDetailsProps>) {
  131. //...
  132. }
  133. ```