@igstack/app-catalog-frontend-core 0.2.0 → 0.3.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 (192) hide show
  1. package/dist/esm/api/infra/trpc.d.ts +0 -1491
  2. package/dist/esm/modules/appCatalog/context/AppCatalogContext.js +1 -0
  3. package/dist/esm/modules/appCatalog/context/AppCatalogContext.js.map +1 -1
  4. package/dist/esm/modules/appCatalog/ui/filters/FilterBar.js +23 -11
  5. package/dist/esm/modules/appCatalog/ui/filters/FilterBar.js.map +1 -1
  6. package/dist/esm/modules/appCatalog/ui/grid/AppCatalogGrid.d.ts +5 -1
  7. package/dist/esm/modules/appCatalog/ui/grid/AppCatalogGrid.js +146 -56
  8. package/dist/esm/modules/appCatalog/ui/grid/AppCatalogGrid.js.map +1 -1
  9. package/dist/esm/modules/appCatalog/ui/pages/AppCatalogPage.js +20 -1
  10. package/dist/esm/modules/appCatalog/ui/pages/AppCatalogPage.js.map +1 -1
  11. package/dist/esm/modules/auth/AuthContext.js +1 -1
  12. package/dist/esm/modules/auth/AuthModalContext.js +1 -1
  13. package/dist/esm/modules/auth/authClient.d.ts +2 -2
  14. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/classic/schemas.js +4 -37
  15. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/classic/schemas.js.map +1 -1
  16. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/api.js +2 -10
  17. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/api.js.map +1 -1
  18. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/checks.js +1 -1
  19. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/json-schema-processors.js +0 -44
  20. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/json-schema-processors.js.map +1 -1
  21. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/parse.js +0 -4
  22. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/parse.js.map +1 -1
  23. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/regexes.js +0 -2
  24. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/regexes.js.map +1 -1
  25. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/schemas.js +4 -49
  26. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/schemas.js.map +1 -1
  27. package/dist/esm/routeTree.gen.d.ts +3 -164
  28. package/dist/esm/routeTree.gen.js +8 -80
  29. package/dist/esm/routeTree.gen.js.map +1 -1
  30. package/dist/esm/ui/button.d.ts +1 -1
  31. package/dist/esm/ui/card.js +1 -48
  32. package/dist/esm/ui/card.js.map +1 -1
  33. package/dist/esm/ui/command.js +1 -15
  34. package/dist/esm/ui/command.js.map +1 -1
  35. package/dist/esm/ui/components/header/Header.js +2 -11
  36. package/dist/esm/ui/components/header/Header.js.map +1 -1
  37. package/dist/esm/ui/input-group.js +125 -0
  38. package/dist/esm/ui/input-group.js.map +1 -0
  39. package/package.json +3 -3
  40. package/src/modules/appCatalog/ui/components/AppDetailModal.tsx +2 -21
  41. package/src/routeTree.gen.ts +2 -220
  42. package/src/ui/components/header/Header.tsx +2 -12
  43. package/dist/esm/components/IconPickerDialog.d.ts +0 -8
  44. package/dist/esm/components/IconPickerDialog.js +0 -98
  45. package/dist/esm/components/IconPickerDialog.js.map +0 -1
  46. package/dist/esm/components/IconPickerField.d.ts +0 -9
  47. package/dist/esm/components/IconPickerField.js +0 -76
  48. package/dist/esm/components/IconPickerField.js.map +0 -1
  49. package/dist/esm/modules/admin-base/components/AdminChat.d.ts +0 -1
  50. package/dist/esm/modules/admin-base/components/AdminChat.js +0 -82
  51. package/dist/esm/modules/admin-base/components/AdminChat.js.map +0 -1
  52. package/dist/esm/modules/admin-base/components/AdminLayout.d.ts +0 -5
  53. package/dist/esm/modules/admin-base/components/AdminLayout.js +0 -83
  54. package/dist/esm/modules/admin-base/components/AdminLayout.js.map +0 -1
  55. package/dist/esm/modules/admin-base/components/AdminWelcome.d.ts +0 -1
  56. package/dist/esm/modules/admin-base/components/AdminWelcome.js +0 -37
  57. package/dist/esm/modules/admin-base/components/AdminWelcome.js.map +0 -1
  58. package/dist/esm/modules/admin-base/context/AdminConfigContext.d.ts +0 -8
  59. package/dist/esm/modules/admin-base/context/AdminConfigContext.js +0 -27
  60. package/dist/esm/modules/admin-base/context/AdminConfigContext.js.map +0 -1
  61. package/dist/esm/modules/admin-base/index.d.ts +0 -5
  62. package/dist/esm/modules/admin-base/types/adminTypes.d.ts +0 -10
  63. package/dist/esm/modules/appCatalog/AppCatalogAdminPage.d.ts +0 -1
  64. package/dist/esm/modules/appCatalog/AppCatalogAdminPage.js +0 -196
  65. package/dist/esm/modules/appCatalog/AppCatalogAdminPage.js.map +0 -1
  66. package/dist/esm/modules/appCatalog/ScreenshotItem.js +0 -57
  67. package/dist/esm/modules/appCatalog/ScreenshotItem.js.map +0 -1
  68. package/dist/esm/modules/appCatalog/ScreenshotManager.js +0 -155
  69. package/dist/esm/modules/appCatalog/ScreenshotManager.js.map +0 -1
  70. package/dist/esm/modules/approvalMethod/AccessRequestFormFields.d.ts +0 -7
  71. package/dist/esm/modules/approvalMethod/AccessRequestFormFields.js +0 -323
  72. package/dist/esm/modules/approvalMethod/AccessRequestFormFields.js.map +0 -1
  73. package/dist/esm/modules/approvalMethod/ApprovalMethodForm.d.ts +0 -14
  74. package/dist/esm/modules/approvalMethod/ApprovalMethodForm.js +0 -227
  75. package/dist/esm/modules/approvalMethod/ApprovalMethodForm.js.map +0 -1
  76. package/dist/esm/modules/approvalMethod/ApprovalMethodSelector.d.ts +0 -7
  77. package/dist/esm/modules/approvalMethod/ApprovalMethodSelector.js +0 -124
  78. package/dist/esm/modules/approvalMethod/ApprovalMethodSelector.js.map +0 -1
  79. package/dist/esm/modules/approvalMethod/api/ApiQueryMagazineApprovalMethod.d.ts +0 -381
  80. package/dist/esm/modules/approvalMethod/api/ApiQueryMagazineApprovalMethod.js +0 -26
  81. package/dist/esm/modules/approvalMethod/api/ApiQueryMagazineApprovalMethod.js.map +0 -1
  82. package/dist/esm/modules/auth/authUtils.js +0 -25
  83. package/dist/esm/modules/auth/authUtils.js.map +0 -1
  84. package/dist/esm/modules/icons/IconManagementPage.d.ts +0 -1
  85. package/dist/esm/modules/icons/IconManagementPage.js +0 -177
  86. package/dist/esm/modules/icons/IconManagementPage.js.map +0 -1
  87. package/dist/esm/node_modules/.pnpm/@dnd-kit_accessibility@3.1.1_react@19.1.2/node_modules/@dnd-kit/accessibility/dist/accessibility.esm.js +0 -60
  88. package/dist/esm/node_modules/.pnpm/@dnd-kit_accessibility@3.1.1_react@19.1.2/node_modules/@dnd-kit/accessibility/dist/accessibility.esm.js.map +0 -1
  89. package/dist/esm/node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.1.2_react@19.1.2__react@19.1.2/node_modules/@dnd-kit/core/dist/core.esm.js +0 -3055
  90. package/dist/esm/node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.1.2_react@19.1.2__react@19.1.2/node_modules/@dnd-kit/core/dist/core.esm.js.map +0 -1
  91. package/dist/esm/node_modules/.pnpm/@dnd-kit_sortable@10.0.0_@dnd-kit_core@6.3.1_react-dom@19.1.2_react@19.1.2__react@19.1.2__react@19.1.2/node_modules/@dnd-kit/sortable/dist/sortable.esm.js +0 -593
  92. package/dist/esm/node_modules/.pnpm/@dnd-kit_sortable@10.0.0_@dnd-kit_core@6.3.1_react-dom@19.1.2_react@19.1.2__react@19.1.2__react@19.1.2/node_modules/@dnd-kit/sortable/dist/sortable.esm.js.map +0 -1
  93. package/dist/esm/node_modules/.pnpm/@dnd-kit_utilities@3.2.2_react@19.1.2/node_modules/@dnd-kit/utilities/dist/utilities.esm.js +0 -302
  94. package/dist/esm/node_modules/.pnpm/@dnd-kit_utilities@3.2.2_react@19.1.2/node_modules/@dnd-kit/utilities/dist/utilities.esm.js.map +0 -1
  95. package/dist/esm/node_modules/.pnpm/@hookform_resolvers@5.2.2_react-hook-form@7.71.1_react@19.1.2_/node_modules/@hookform/resolvers/dist/resolvers.js +0 -34
  96. package/dist/esm/node_modules/.pnpm/@hookform_resolvers@5.2.2_react-hook-form@7.71.1_react@19.1.2_/node_modules/@hookform/resolvers/dist/resolvers.js.map +0 -1
  97. package/dist/esm/node_modules/.pnpm/@hookform_resolvers@5.2.2_react-hook-form@7.71.1_react@19.1.2_/node_modules/@hookform/resolvers/zod/dist/zod.js +0 -94
  98. package/dist/esm/node_modules/.pnpm/@hookform_resolvers@5.2.2_react-hook-form@7.71.1_react@19.1.2_/node_modules/@hookform/resolvers/zod/dist/zod.js.map +0 -1
  99. package/dist/esm/node_modules/.pnpm/react-hook-form@7.71.1_react@19.1.2/node_modules/react-hook-form/dist/index.esm.js +0 -1894
  100. package/dist/esm/node_modules/.pnpm/react-hook-form@7.71.1_react@19.1.2/node_modules/react-hook-form/dist/index.esm.js.map +0 -1
  101. package/dist/esm/routes/admin/app-for-catalog/$id.d.ts +0 -5
  102. package/dist/esm/routes/admin/app-for-catalog/_id.js +0 -67
  103. package/dist/esm/routes/admin/app-for-catalog/_id.js.map +0 -1
  104. package/dist/esm/routes/admin/app-for-catalog/_id2.js +0 -321
  105. package/dist/esm/routes/admin/app-for-catalog/_id2.js.map +0 -1
  106. package/dist/esm/routes/admin/app-for-catalog/index.d.ts +0 -1
  107. package/dist/esm/routes/admin/app-for-catalog/index.js +0 -9
  108. package/dist/esm/routes/admin/app-for-catalog/index.js.map +0 -1
  109. package/dist/esm/routes/admin/app-for-catalog/index2.js +0 -12
  110. package/dist/esm/routes/admin/app-for-catalog/index2.js.map +0 -1
  111. package/dist/esm/routes/admin/app-for-catalog.d.ts +0 -1
  112. package/dist/esm/routes/admin/app-for-catalog.js +0 -14
  113. package/dist/esm/routes/admin/app-for-catalog.js.map +0 -1
  114. package/dist/esm/routes/admin/app-for-catalog2.js +0 -9
  115. package/dist/esm/routes/admin/app-for-catalog2.js.map +0 -1
  116. package/dist/esm/routes/admin/approval-methods/index.d.ts +0 -32
  117. package/dist/esm/routes/admin/approval-methods/index.js +0 -24
  118. package/dist/esm/routes/admin/approval-methods/index.js.map +0 -1
  119. package/dist/esm/routes/admin/approval-methods/index2.js +0 -100
  120. package/dist/esm/routes/admin/approval-methods/index2.js.map +0 -1
  121. package/dist/esm/routes/admin/approval-methods.d.ts +0 -1
  122. package/dist/esm/routes/admin/approval-methods.js +0 -14
  123. package/dist/esm/routes/admin/approval-methods.js.map +0 -1
  124. package/dist/esm/routes/admin/approval-methods2.js +0 -7
  125. package/dist/esm/routes/admin/approval-methods2.js.map +0 -1
  126. package/dist/esm/routes/admin/chat.d.ts +0 -1
  127. package/dist/esm/routes/admin/chat.js +0 -14
  128. package/dist/esm/routes/admin/chat.js.map +0 -1
  129. package/dist/esm/routes/admin/chat2.js +0 -9
  130. package/dist/esm/routes/admin/chat2.js.map +0 -1
  131. package/dist/esm/routes/admin/icons.d.ts +0 -1
  132. package/dist/esm/routes/admin/icons.js +0 -14
  133. package/dist/esm/routes/admin/icons.js.map +0 -1
  134. package/dist/esm/routes/admin/icons2.js +0 -12
  135. package/dist/esm/routes/admin/icons2.js.map +0 -1
  136. package/dist/esm/routes/admin/index.d.ts +0 -1
  137. package/dist/esm/routes/admin/index.js +0 -9
  138. package/dist/esm/routes/admin/index.js.map +0 -1
  139. package/dist/esm/routes/admin/index2.js +0 -9
  140. package/dist/esm/routes/admin/index2.js.map +0 -1
  141. package/dist/esm/routes/admin.d.ts +0 -1
  142. package/dist/esm/routes/admin.js +0 -37
  143. package/dist/esm/routes/admin.js.map +0 -1
  144. package/dist/esm/routes/admin2.js +0 -18
  145. package/dist/esm/routes/admin2.js.map +0 -1
  146. package/dist/esm/ui/alert-dialog.js +0 -141
  147. package/dist/esm/ui/alert-dialog.js.map +0 -1
  148. package/dist/esm/ui/breadcrumb.js +0 -84
  149. package/dist/esm/ui/breadcrumb.js.map +0 -1
  150. package/dist/esm/ui/components/Breadcrumbs.js +0 -36
  151. package/dist/esm/ui/components/Breadcrumbs.js.map +0 -1
  152. package/dist/esm/ui/crud-list/CrudList.js +0 -189
  153. package/dist/esm/ui/crud-list/CrudList.js.map +0 -1
  154. package/dist/esm/ui/editable-list/EditableListField.js +0 -130
  155. package/dist/esm/ui/editable-list/EditableListField.js.map +0 -1
  156. package/dist/esm/ui/form.js +0 -134
  157. package/dist/esm/ui/form.js.map +0 -1
  158. package/dist/esm/ui/linkExternal.js +0 -26
  159. package/dist/esm/ui/linkExternal.js.map +0 -1
  160. package/dist/esm/ui/markdown-editor/MarkdownEditor.js +0 -116
  161. package/dist/esm/ui/markdown-editor/MarkdownEditor.js.map +0 -1
  162. package/dist/esm/ui/markdown-editor/MarkdownToolbar.js +0 -99
  163. package/dist/esm/ui/markdown-editor/MarkdownToolbar.js.map +0 -1
  164. package/dist/esm/ui/scroll-area.js +0 -62
  165. package/dist/esm/ui/scroll-area.js.map +0 -1
  166. package/dist/esm/ui/select.js +0 -138
  167. package/dist/esm/ui/select.js.map +0 -1
  168. package/dist/esm/ui/textarea.js +0 -19
  169. package/dist/esm/ui/textarea.js.map +0 -1
  170. package/src/components/IconPickerDialog.tsx +0 -136
  171. package/src/components/IconPickerField.tsx +0 -88
  172. package/src/modules/admin-base/components/AdminChat.tsx +0 -122
  173. package/src/modules/admin-base/components/AdminLayout.tsx +0 -111
  174. package/src/modules/admin-base/components/AdminWelcome.tsx +0 -52
  175. package/src/modules/admin-base/context/AdminConfigContext.tsx +0 -36
  176. package/src/modules/admin-base/index.ts +0 -16
  177. package/src/modules/admin-base/types/adminTypes.ts +0 -11
  178. package/src/modules/appCatalog/AppCatalogAdminPage.tsx +0 -274
  179. package/src/modules/approvalMethod/AccessRequestFormFields.tsx +0 -393
  180. package/src/modules/approvalMethod/ApprovalMethodForm.tsx +0 -323
  181. package/src/modules/approvalMethod/ApprovalMethodSelector.tsx +0 -150
  182. package/src/modules/approvalMethod/api/ApiQueryMagazineApprovalMethod.ts +0 -34
  183. package/src/modules/icons/IconManagementPage.tsx +0 -245
  184. package/src/routes/admin/app-for-catalog/$id.tsx +0 -571
  185. package/src/routes/admin/app-for-catalog/index.tsx +0 -19
  186. package/src/routes/admin/app-for-catalog.tsx +0 -12
  187. package/src/routes/admin/approval-methods/index.tsx +0 -161
  188. package/src/routes/admin/approval-methods.tsx +0 -10
  189. package/src/routes/admin/chat.tsx +0 -13
  190. package/src/routes/admin/icons.tsx +0 -22
  191. package/src/routes/admin/index.tsx +0 -9
  192. package/src/routes/admin.tsx +0 -60
@@ -1,571 +0,0 @@
1
- import { zodResolver } from '@hookform/resolvers/zod'
2
- import { useMutation, useQueryClient } from '@tanstack/react-query'
3
- import { createFileRoute, useNavigate } from '@tanstack/react-router'
4
- import type { Control, FieldValues } from 'react-hook-form'
5
- import { useForm } from 'react-hook-form'
6
- import { z } from 'zod'
7
- import { useTRPC } from '~/api/infra/trpc'
8
- import { IconPickerField } from '~/components/IconPickerField'
9
- import type { Screenshot } from '~/modules/appCatalog/ScreenshotManager'
10
- import { ScreenshotManager } from '~/modules/appCatalog/ScreenshotManager'
11
- import { ApprovalMethodSelector } from '~/modules/approvalMethod/ApprovalMethodSelector'
12
- import { AccessRequestFormFields } from '~/modules/approvalMethod/AccessRequestFormFields'
13
- import { Button } from '~/ui/button'
14
- import {
15
- Card,
16
- CardContent,
17
- CardDescription,
18
- CardHeader,
19
- CardTitle,
20
- } from '~/ui/card'
21
- import {
22
- Form,
23
- FormControl,
24
- FormDescription,
25
- FormField,
26
- FormItem,
27
- FormLabel,
28
- FormMessage,
29
- } from '~/ui/form'
30
- import { Input } from '~/ui/input'
31
- import { Textarea } from '~/ui/textarea'
32
-
33
- export const Route = createFileRoute('/admin/app-for-catalog/$id')({
34
- component: RouteComponent,
35
- async loader({ params, context }) {
36
- const { id } = params
37
- const { trpcClient } = context
38
-
39
- const app =
40
- id === 'new'
41
- ? null
42
- : await trpcClient.appCatalogAdmin.getBySlug.query({ slug: id })
43
-
44
- return { app }
45
- },
46
- staticData: {
47
- breadcrumb: {
48
- title: 'Edit App',
49
- },
50
- },
51
- })
52
-
53
- type AccessType =
54
- | 'none'
55
- | 'bot'
56
- | 'ticketing'
57
- | 'email'
58
- | 'self-service'
59
- | 'documentation'
60
- | 'manual'
61
-
62
- type ValidAccessType = Exclude<AccessType, 'none'>
63
-
64
- const formSchema = z.object({
65
- slug: z
66
- .string()
67
- .min(1, 'Slug is required')
68
- .regex(
69
- /^[a-z0-9-]+$/,
70
- 'Slug must contain only lowercase letters, numbers, and hyphens',
71
- ),
72
- displayName: z.string().min(1, 'Display name is required'),
73
- description: z.string().min(1, 'Description is required'),
74
- accessType: z
75
- .enum([
76
- 'none',
77
- 'self-service',
78
- 'ticketing',
79
- 'bot',
80
- 'email',
81
- 'documentation',
82
- 'manual',
83
- ])
84
- .default('none'),
85
- // Access method fields
86
- accessUrl: z.string().url('Must be a valid URL').optional().or(z.literal('')),
87
- accessInstructions: z.string().optional(),
88
- iconName: z.string().optional(),
89
- appUrl: z.string().url('Must be a valid URL').optional().or(z.literal('')),
90
- notes: z.string().optional(),
91
- links: z.string().default('[]'),
92
- // Approval Method fields
93
- hasApproval: z.boolean().default(false),
94
- approvalMethodId: z.string().optional(),
95
- accessRequest: z
96
- .object({
97
- approvalMethodId: z.string().optional(),
98
- comments: z.string().optional(),
99
- requestPrompt: z.string().optional(),
100
- postApprovalInstructions: z.string().optional(),
101
- roles: z
102
- .array(
103
- z.object({ name: z.string(), description: z.string().optional() }),
104
- )
105
- .optional(),
106
- approvers: z
107
- .array(
108
- z.object({ displayName: z.string(), contact: z.string().optional() }),
109
- )
110
- .optional(),
111
- urls: z
112
- .array(z.object({ label: z.string().optional(), url: z.string() }))
113
- .optional(),
114
- whoToReachOut: z.string().optional(),
115
- })
116
- .optional(),
117
- })
118
-
119
- type FormData = z.infer<typeof formSchema>
120
-
121
- function RouteComponent() {
122
- const { id } = Route.useParams()
123
- const navigate = useNavigate()
124
- const trpc = useTRPC()
125
- const queryClient = useQueryClient()
126
- const loaderData = Route.useLoaderData()
127
- const isNew = id === 'new'
128
-
129
- const app = loaderData.app
130
-
131
- const accessRequest = app?.accessRequest as any
132
- const hasApproval = !!accessRequest
133
-
134
- const form = useForm<FormData>({
135
- resolver: zodResolver(formSchema as unknown as never),
136
- defaultValues: app
137
- ? {
138
- slug: app.slug || '',
139
- displayName: app.displayName || '',
140
- description: app.description || '',
141
- accessUrl: (app.access as { url?: string } | undefined)?.url || '',
142
- accessInstructions:
143
- (app.access as { instructions?: string } | undefined)
144
- ?.instructions || '',
145
- iconName: app.iconName || '',
146
- appUrl: app.appUrl || '',
147
- notes: app.notes || '',
148
- links: JSON.stringify(
149
- (app as { links?: unknown }).links || [],
150
- null,
151
- 2,
152
- ),
153
- hasApproval,
154
- approvalMethodId: accessRequest?.approvalMethodId,
155
- accessRequest: hasApproval ? accessRequest : undefined,
156
- }
157
- : {
158
- slug: '',
159
- displayName: '',
160
- description: '',
161
- accessUrl: '',
162
- accessInstructions: '',
163
- iconName: '',
164
- appUrl: '',
165
- notes: '',
166
- links: '[]',
167
- hasApproval: false,
168
- approvalMethodId: undefined,
169
- accessRequest: undefined,
170
- },
171
- })
172
- const formControl = form.control as unknown as Control<FieldValues>
173
-
174
- // Watch form values for reactivity
175
- const hasApprovalValue = form.watch('hasApproval')
176
- const approvalMethodIdValue = form.watch('approvalMethodId')
177
-
178
- const createMutation = useMutation({
179
- ...trpc.appCatalogAdmin.create.mutationOptions(),
180
- onSuccess: () => {
181
- queryClient.invalidateQueries({
182
- queryKey: trpc.appCatalogAdmin.list.queryKey(),
183
- })
184
- navigate({ to: '/admin/app-for-catalog' })
185
- },
186
- })
187
-
188
- const updateMutation = useMutation({
189
- ...trpc.appCatalogAdmin.update.mutationOptions(),
190
- onSuccess: () => {
191
- queryClient.invalidateQueries({
192
- queryKey: trpc.appCatalogAdmin.list.queryKey(),
193
- })
194
- navigate({ to: '/admin/app-for-catalog' })
195
- },
196
- })
197
-
198
- const updateScreenshotsMutation = useMutation({
199
- ...trpc.appCatalogAdmin.update.mutationOptions(),
200
- onSuccess: () => {
201
- queryClient.invalidateQueries({
202
- queryKey: trpc.appCatalogAdmin.list.queryKey(),
203
- })
204
- queryClient.invalidateQueries({
205
- queryKey: trpc.appCatalogAdmin.getBySlug.queryKey({ slug: id }),
206
- })
207
- },
208
- })
209
-
210
- const onSubmit = (formData: FormData) => {
211
- try {
212
- type AppLink = { displayName?: string; url: string }
213
- const linksData: unknown = JSON.parse(formData.links)
214
- const links: Array<AppLink> | undefined = Array.isArray(linksData)
215
- ? linksData.filter(
216
- (l): l is AppLink =>
217
- typeof l === 'object' &&
218
- l !== null &&
219
- 'url' in l &&
220
- typeof (l as { url?: unknown }).url === 'string',
221
- )
222
- : undefined
223
-
224
- // Only create access object if type is not 'none'
225
- const access =
226
- formData.accessType !== 'none'
227
- ? ({
228
- type: formData.accessType,
229
- ...(formData.accessUrl && { url: formData.accessUrl }),
230
- ...(formData.accessInstructions && {
231
- instructions: formData.accessInstructions,
232
- }),
233
- } as {
234
- type: ValidAccessType
235
- } & Record<string, unknown>)
236
- : undefined
237
-
238
- // Process approval details
239
- let accessRequestPayload: any = undefined
240
- if (
241
- formData.hasApproval &&
242
- formData.approvalMethodId &&
243
- formData.accessRequest
244
- ) {
245
- accessRequestPayload = {
246
- approvalMethodId: formData.approvalMethodId,
247
- comments: formData.accessRequest.comments || undefined,
248
- requestPrompt: formData.accessRequest.requestPrompt || undefined,
249
- postApprovalInstructions:
250
- formData.accessRequest.postApprovalInstructions || undefined,
251
- roles: formData.accessRequest.roles?.length
252
- ? formData.accessRequest.roles
253
- : undefined,
254
- approvers: formData.accessRequest.approvers?.length
255
- ? formData.accessRequest.approvers
256
- : undefined,
257
- urls: formData.accessRequest.urls?.length
258
- ? formData.accessRequest.urls
259
- : undefined,
260
- whoToReachOut: formData.accessRequest.whoToReachOut || undefined,
261
- }
262
- }
263
-
264
- const payload = {
265
- slug: formData.slug,
266
- displayName: formData.displayName,
267
- description: formData.description,
268
- access,
269
- iconName: formData.iconName || undefined,
270
- appUrl: formData.appUrl || undefined,
271
- notes: formData.notes || undefined,
272
- links: links && links.length > 0 ? links : undefined,
273
- accessRequest: accessRequestPayload,
274
- }
275
-
276
- if (isNew) {
277
- createMutation.mutate(payload)
278
- } else {
279
- if (!app?.id) {
280
- throw new Error('App ID is required for update')
281
- }
282
- updateMutation.mutate({
283
- id: app.id,
284
- ...payload,
285
- })
286
- }
287
- } catch (error) {
288
- form.setError('links', {
289
- type: 'validate',
290
- message:
291
- error instanceof Error
292
- ? error.message
293
- : 'Invalid JSON in form fields',
294
- })
295
- }
296
- }
297
-
298
- const isPending = createMutation.isPending || updateMutation.isPending
299
-
300
- return (
301
- <div className="flex-1 flex flex-col">
302
- <div className="border-b px-6 py-4 flex items-center justify-between">
303
- <h2 className="text-xl font-semibold">
304
- {isNew ? 'Create New App' : `Edit: ${app?.displayName || ''}`}
305
- </h2>
306
- <Button
307
- variant="outline"
308
- onClick={() => navigate({ to: '/admin/app-for-catalog' })}
309
- >
310
- Back to List
311
- </Button>
312
- </div>
313
-
314
- <div className="flex-1 overflow-auto p-6">
315
- <div className="max-w-4xl space-y-6">
316
- <Card>
317
- <CardHeader>
318
- <CardTitle>App Details</CardTitle>
319
- <CardDescription>
320
- Configure the app catalog entry with all necessary information
321
- </CardDescription>
322
- </CardHeader>
323
- <CardContent>
324
- <Form {...form}>
325
- <form
326
- onSubmit={form.handleSubmit(onSubmit)}
327
- className="space-y-6"
328
- >
329
- <div className="grid grid-cols-2 gap-4">
330
- <FormField
331
- control={formControl}
332
- name="slug"
333
- render={({ field }) => (
334
- <FormItem>
335
- <FormLabel>Slug (URL identifier) *</FormLabel>
336
- <FormControl>
337
- <Input placeholder="my-app" {...field} />
338
- </FormControl>
339
- <FormDescription>
340
- Lowercase letters, numbers, and hyphens only
341
- </FormDescription>
342
- <FormMessage />
343
- </FormItem>
344
- )}
345
- />
346
-
347
- <FormField
348
- control={formControl}
349
- name="displayName"
350
- render={({ field }) => (
351
- <FormItem>
352
- <FormLabel>Display Name *</FormLabel>
353
- <FormControl>
354
- <Input placeholder="My Application" {...field} />
355
- </FormControl>
356
- <FormMessage />
357
- </FormItem>
358
- )}
359
- />
360
- </div>
361
-
362
- <FormField
363
- control={formControl}
364
- name="description"
365
- render={({ field }) => (
366
- <FormItem>
367
- <FormLabel>Description *</FormLabel>
368
- <FormControl>
369
- <Textarea
370
- placeholder="Enter app description..."
371
- rows={3}
372
- {...field}
373
- />
374
- </FormControl>
375
- <FormMessage />
376
- </FormItem>
377
- )}
378
- />
379
-
380
- <div className="grid grid-cols-2 gap-4">
381
- <FormField
382
- control={formControl}
383
- name="iconName"
384
- render={({ field }) => (
385
- <FormItem>
386
- <FormControl>
387
- <IconPickerField
388
- value={field.value}
389
- onChange={field.onChange}
390
- placeholder="Select an icon..."
391
- />
392
- </FormControl>
393
- <FormMessage />
394
- </FormItem>
395
- )}
396
- />
397
- </div>
398
-
399
- <FormField
400
- control={formControl}
401
- name="appUrl"
402
- render={({ field }) => (
403
- <FormItem>
404
- <FormLabel>App URL</FormLabel>
405
- <FormControl>
406
- <Input
407
- type="url"
408
- placeholder="https://..."
409
- {...field}
410
- />
411
- </FormControl>
412
- <FormMessage />
413
- </FormItem>
414
- )}
415
- />
416
-
417
- <FormField
418
- control={formControl}
419
- name="notes"
420
- render={({ field }) => (
421
- <FormItem>
422
- <FormLabel>Notes</FormLabel>
423
- <FormControl>
424
- <Textarea
425
- placeholder="Any additional notes..."
426
- rows={2}
427
- {...field}
428
- />
429
- </FormControl>
430
- <FormMessage />
431
- </FormItem>
432
- )}
433
- />
434
-
435
- <FormField
436
- control={formControl}
437
- name="links"
438
- render={({ field }) => (
439
- <FormItem>
440
- <FormLabel>Links (JSON)</FormLabel>
441
- <FormControl>
442
- <Textarea
443
- placeholder={`[\n { "displayName": "Documentation", "url": "https://..." },\n { "url": "https://..." }\n]`}
444
- rows={5}
445
- className="font-mono text-sm"
446
- {...field}
447
- />
448
- </FormControl>
449
- <FormDescription>
450
- Array of link objects with url and optional
451
- displayName
452
- </FormDescription>
453
- <FormMessage />
454
- </FormItem>
455
- )}
456
- />
457
-
458
- {/* Approval Configuration */}
459
- <div className="pt-6 border-t">
460
- <div className="space-y-4">
461
- <FormField
462
- control={formControl}
463
- name="hasApproval"
464
- render={({ field }) => (
465
- <FormItem className="flex items-center gap-2">
466
- <FormControl>
467
- <input
468
- type="checkbox"
469
- checked={field.value}
470
- onChange={field.onChange}
471
- className="h-4 w-4"
472
- />
473
- </FormControl>
474
- <FormLabel className="!mt-0">
475
- Configure Approval
476
- </FormLabel>
477
- </FormItem>
478
- )}
479
- />
480
-
481
- {hasApprovalValue && (
482
- <div className="space-y-4 pl-6">
483
- <FormField
484
- control={formControl}
485
- name="approvalMethodId"
486
- render={({ field }) => (
487
- <FormItem>
488
- <FormLabel>Approval Method *</FormLabel>
489
- <FormControl>
490
- <ApprovalMethodSelector
491
- value={field.value}
492
- onChange={field.onChange}
493
- />
494
- </FormControl>
495
- <FormMessage />
496
- </FormItem>
497
- )}
498
- />
499
-
500
- <AccessRequestFormFields
501
- control={formControl}
502
- approvalMethodId={approvalMethodIdValue}
503
- />
504
- </div>
505
- )}
506
- </div>
507
- </div>
508
-
509
- <div className="flex gap-2 pt-4">
510
- <Button type="submit" disabled={isPending}>
511
- {isPending
512
- ? 'Saving...'
513
- : isNew
514
- ? 'Create App'
515
- : 'Save Changes'}
516
- </Button>
517
- <Button
518
- type="button"
519
- variant="outline"
520
- onClick={() => navigate({ to: '/admin/app-for-catalog' })}
521
- >
522
- Cancel
523
- </Button>
524
- </div>
525
- </form>
526
- </Form>
527
- </CardContent>
528
- </Card>
529
-
530
- {!isNew && (
531
- <ScreenshotManager
532
- screenshots={(
533
- (app as { screenshotIds?: Array<string> }).screenshotIds || []
534
- ).map((screenshotId) => ({
535
- id: screenshotId,
536
- url: `/api/assets/${screenshotId}`,
537
- }))}
538
- onAddScreenshot={async (file) => {
539
- const formData = new FormData()
540
- formData.append('asset', file)
541
- formData.append('name', `${app?.id ?? 'app'}-${file.name}`)
542
- formData.append('assetType', 'screenshot')
543
-
544
- const response = await fetch('/api/assets/upload', {
545
- method: 'POST',
546
- body: formData,
547
- })
548
-
549
- if (!response.ok) {
550
- throw new Error('Failed to upload screenshot')
551
- }
552
-
553
- const data: { id: string } = await response.json()
554
- return { id: data.id, url: `/api/assets/${data.id}` }
555
- }}
556
- onScreenshotsChange={(screenshots: Array<Screenshot>) => {
557
- if (!app?.id) {
558
- throw new Error('App ID is required for screenshot update')
559
- }
560
- updateScreenshotsMutation.mutate({
561
- id: app.id,
562
- screenshotIds: screenshots.map((s) => s.id),
563
- })
564
- }}
565
- />
566
- )}
567
- </div>
568
- </div>
569
- </div>
570
- )
571
- }
@@ -1,19 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
- import { AppCatalogAdminPage } from '~/modules/appCatalog/AppCatalogAdminPage'
3
-
4
- export const Route = createFileRoute('/admin/app-for-catalog/')({
5
- component: RouteComponent,
6
- })
7
-
8
- function RouteComponent() {
9
- return (
10
- <div className="flex-1 flex flex-col">
11
- <div className="border-b px-6 py-4">
12
- <h2 className="text-xl font-semibold">App Catalog</h2>
13
- </div>
14
- <div className="flex-1 overflow-auto p-6">
15
- <AppCatalogAdminPage />
16
- </div>
17
- </div>
18
- )
19
- }
@@ -1,12 +0,0 @@
1
- import { Outlet, createFileRoute } from '@tanstack/react-router'
2
-
3
- export const Route = createFileRoute('/admin/app-for-catalog')({
4
- component: AppCatalogLayoutRoute,
5
- staticData: {
6
- breadcrumb: { title: 'App Catalog' },
7
- },
8
- })
9
-
10
- function AppCatalogLayoutRoute() {
11
- return <Outlet />
12
- }