@igstack/app-catalog-frontend-core 0.2.0 → 0.3.1-alpha-20260305175850

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 (190) hide show
  1. package/dist/esm/api/infra/trpc.d.ts +0 -1491
  2. package/dist/esm/modules/appCatalog/ui/filters/FilterBar.js +23 -11
  3. package/dist/esm/modules/appCatalog/ui/filters/FilterBar.js.map +1 -1
  4. package/dist/esm/modules/appCatalog/ui/grid/AppCatalogGrid.d.ts +5 -1
  5. package/dist/esm/modules/appCatalog/ui/grid/AppCatalogGrid.js +146 -56
  6. package/dist/esm/modules/appCatalog/ui/grid/AppCatalogGrid.js.map +1 -1
  7. package/dist/esm/modules/appCatalog/ui/pages/AppCatalogPage.js +20 -1
  8. package/dist/esm/modules/appCatalog/ui/pages/AppCatalogPage.js.map +1 -1
  9. package/dist/esm/modules/auth/AuthContext.js +1 -1
  10. package/dist/esm/modules/auth/AuthModalContext.js +1 -1
  11. package/dist/esm/modules/auth/authClient.d.ts +2 -2
  12. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/classic/schemas.js +4 -37
  13. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/classic/schemas.js.map +1 -1
  14. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/api.js +2 -10
  15. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/api.js.map +1 -1
  16. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/checks.js +1 -1
  17. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/json-schema-processors.js +0 -44
  18. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/json-schema-processors.js.map +1 -1
  19. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/parse.js +0 -4
  20. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/parse.js.map +1 -1
  21. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/regexes.js +0 -2
  22. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/regexes.js.map +1 -1
  23. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/schemas.js +4 -49
  24. package/dist/esm/node_modules/.pnpm/zod@4.3.5/node_modules/zod/v4/core/schemas.js.map +1 -1
  25. package/dist/esm/routeTree.gen.d.ts +3 -164
  26. package/dist/esm/routeTree.gen.js +8 -80
  27. package/dist/esm/routeTree.gen.js.map +1 -1
  28. package/dist/esm/ui/button.d.ts +1 -1
  29. package/dist/esm/ui/card.js +1 -48
  30. package/dist/esm/ui/card.js.map +1 -1
  31. package/dist/esm/ui/command.js +1 -15
  32. package/dist/esm/ui/command.js.map +1 -1
  33. package/dist/esm/ui/components/header/Header.js +2 -11
  34. package/dist/esm/ui/components/header/Header.js.map +1 -1
  35. package/dist/esm/ui/input-group.js +125 -0
  36. package/dist/esm/ui/input-group.js.map +1 -0
  37. package/package.json +3 -3
  38. package/src/modules/appCatalog/ui/components/AppDetailModal.tsx +2 -21
  39. package/src/routeTree.gen.ts +2 -220
  40. package/src/ui/components/header/Header.tsx +2 -12
  41. package/dist/esm/components/IconPickerDialog.d.ts +0 -8
  42. package/dist/esm/components/IconPickerDialog.js +0 -98
  43. package/dist/esm/components/IconPickerDialog.js.map +0 -1
  44. package/dist/esm/components/IconPickerField.d.ts +0 -9
  45. package/dist/esm/components/IconPickerField.js +0 -76
  46. package/dist/esm/components/IconPickerField.js.map +0 -1
  47. package/dist/esm/modules/admin-base/components/AdminChat.d.ts +0 -1
  48. package/dist/esm/modules/admin-base/components/AdminChat.js +0 -82
  49. package/dist/esm/modules/admin-base/components/AdminChat.js.map +0 -1
  50. package/dist/esm/modules/admin-base/components/AdminLayout.d.ts +0 -5
  51. package/dist/esm/modules/admin-base/components/AdminLayout.js +0 -83
  52. package/dist/esm/modules/admin-base/components/AdminLayout.js.map +0 -1
  53. package/dist/esm/modules/admin-base/components/AdminWelcome.d.ts +0 -1
  54. package/dist/esm/modules/admin-base/components/AdminWelcome.js +0 -37
  55. package/dist/esm/modules/admin-base/components/AdminWelcome.js.map +0 -1
  56. package/dist/esm/modules/admin-base/context/AdminConfigContext.d.ts +0 -8
  57. package/dist/esm/modules/admin-base/context/AdminConfigContext.js +0 -27
  58. package/dist/esm/modules/admin-base/context/AdminConfigContext.js.map +0 -1
  59. package/dist/esm/modules/admin-base/index.d.ts +0 -5
  60. package/dist/esm/modules/admin-base/types/adminTypes.d.ts +0 -10
  61. package/dist/esm/modules/appCatalog/AppCatalogAdminPage.d.ts +0 -1
  62. package/dist/esm/modules/appCatalog/AppCatalogAdminPage.js +0 -196
  63. package/dist/esm/modules/appCatalog/AppCatalogAdminPage.js.map +0 -1
  64. package/dist/esm/modules/appCatalog/ScreenshotItem.js +0 -57
  65. package/dist/esm/modules/appCatalog/ScreenshotItem.js.map +0 -1
  66. package/dist/esm/modules/appCatalog/ScreenshotManager.js +0 -155
  67. package/dist/esm/modules/appCatalog/ScreenshotManager.js.map +0 -1
  68. package/dist/esm/modules/approvalMethod/AccessRequestFormFields.d.ts +0 -7
  69. package/dist/esm/modules/approvalMethod/AccessRequestFormFields.js +0 -323
  70. package/dist/esm/modules/approvalMethod/AccessRequestFormFields.js.map +0 -1
  71. package/dist/esm/modules/approvalMethod/ApprovalMethodForm.d.ts +0 -14
  72. package/dist/esm/modules/approvalMethod/ApprovalMethodForm.js +0 -227
  73. package/dist/esm/modules/approvalMethod/ApprovalMethodForm.js.map +0 -1
  74. package/dist/esm/modules/approvalMethod/ApprovalMethodSelector.d.ts +0 -7
  75. package/dist/esm/modules/approvalMethod/ApprovalMethodSelector.js +0 -124
  76. package/dist/esm/modules/approvalMethod/ApprovalMethodSelector.js.map +0 -1
  77. package/dist/esm/modules/approvalMethod/api/ApiQueryMagazineApprovalMethod.d.ts +0 -381
  78. package/dist/esm/modules/approvalMethod/api/ApiQueryMagazineApprovalMethod.js +0 -26
  79. package/dist/esm/modules/approvalMethod/api/ApiQueryMagazineApprovalMethod.js.map +0 -1
  80. package/dist/esm/modules/auth/authUtils.js +0 -25
  81. package/dist/esm/modules/auth/authUtils.js.map +0 -1
  82. package/dist/esm/modules/icons/IconManagementPage.d.ts +0 -1
  83. package/dist/esm/modules/icons/IconManagementPage.js +0 -177
  84. package/dist/esm/modules/icons/IconManagementPage.js.map +0 -1
  85. 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
  86. 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
  87. 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
  88. 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
  89. 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
  90. 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
  91. 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
  92. 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
  93. 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
  94. 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
  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/zod/dist/zod.js +0 -94
  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/zod/dist/zod.js.map +0 -1
  97. 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
  98. 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
  99. package/dist/esm/routes/admin/app-for-catalog/$id.d.ts +0 -5
  100. package/dist/esm/routes/admin/app-for-catalog/_id.js +0 -67
  101. package/dist/esm/routes/admin/app-for-catalog/_id.js.map +0 -1
  102. package/dist/esm/routes/admin/app-for-catalog/_id2.js +0 -321
  103. package/dist/esm/routes/admin/app-for-catalog/_id2.js.map +0 -1
  104. package/dist/esm/routes/admin/app-for-catalog/index.d.ts +0 -1
  105. package/dist/esm/routes/admin/app-for-catalog/index.js +0 -9
  106. package/dist/esm/routes/admin/app-for-catalog/index.js.map +0 -1
  107. package/dist/esm/routes/admin/app-for-catalog/index2.js +0 -12
  108. package/dist/esm/routes/admin/app-for-catalog/index2.js.map +0 -1
  109. package/dist/esm/routes/admin/app-for-catalog.d.ts +0 -1
  110. package/dist/esm/routes/admin/app-for-catalog.js +0 -14
  111. package/dist/esm/routes/admin/app-for-catalog.js.map +0 -1
  112. package/dist/esm/routes/admin/app-for-catalog2.js +0 -9
  113. package/dist/esm/routes/admin/app-for-catalog2.js.map +0 -1
  114. package/dist/esm/routes/admin/approval-methods/index.d.ts +0 -32
  115. package/dist/esm/routes/admin/approval-methods/index.js +0 -24
  116. package/dist/esm/routes/admin/approval-methods/index.js.map +0 -1
  117. package/dist/esm/routes/admin/approval-methods/index2.js +0 -100
  118. package/dist/esm/routes/admin/approval-methods/index2.js.map +0 -1
  119. package/dist/esm/routes/admin/approval-methods.d.ts +0 -1
  120. package/dist/esm/routes/admin/approval-methods.js +0 -14
  121. package/dist/esm/routes/admin/approval-methods.js.map +0 -1
  122. package/dist/esm/routes/admin/approval-methods2.js +0 -7
  123. package/dist/esm/routes/admin/approval-methods2.js.map +0 -1
  124. package/dist/esm/routes/admin/chat.d.ts +0 -1
  125. package/dist/esm/routes/admin/chat.js +0 -14
  126. package/dist/esm/routes/admin/chat.js.map +0 -1
  127. package/dist/esm/routes/admin/chat2.js +0 -9
  128. package/dist/esm/routes/admin/chat2.js.map +0 -1
  129. package/dist/esm/routes/admin/icons.d.ts +0 -1
  130. package/dist/esm/routes/admin/icons.js +0 -14
  131. package/dist/esm/routes/admin/icons.js.map +0 -1
  132. package/dist/esm/routes/admin/icons2.js +0 -12
  133. package/dist/esm/routes/admin/icons2.js.map +0 -1
  134. package/dist/esm/routes/admin/index.d.ts +0 -1
  135. package/dist/esm/routes/admin/index.js +0 -9
  136. package/dist/esm/routes/admin/index.js.map +0 -1
  137. package/dist/esm/routes/admin/index2.js +0 -9
  138. package/dist/esm/routes/admin/index2.js.map +0 -1
  139. package/dist/esm/routes/admin.d.ts +0 -1
  140. package/dist/esm/routes/admin.js +0 -37
  141. package/dist/esm/routes/admin.js.map +0 -1
  142. package/dist/esm/routes/admin2.js +0 -18
  143. package/dist/esm/routes/admin2.js.map +0 -1
  144. package/dist/esm/ui/alert-dialog.js +0 -141
  145. package/dist/esm/ui/alert-dialog.js.map +0 -1
  146. package/dist/esm/ui/breadcrumb.js +0 -84
  147. package/dist/esm/ui/breadcrumb.js.map +0 -1
  148. package/dist/esm/ui/components/Breadcrumbs.js +0 -36
  149. package/dist/esm/ui/components/Breadcrumbs.js.map +0 -1
  150. package/dist/esm/ui/crud-list/CrudList.js +0 -189
  151. package/dist/esm/ui/crud-list/CrudList.js.map +0 -1
  152. package/dist/esm/ui/editable-list/EditableListField.js +0 -130
  153. package/dist/esm/ui/editable-list/EditableListField.js.map +0 -1
  154. package/dist/esm/ui/form.js +0 -134
  155. package/dist/esm/ui/form.js.map +0 -1
  156. package/dist/esm/ui/linkExternal.js +0 -26
  157. package/dist/esm/ui/linkExternal.js.map +0 -1
  158. package/dist/esm/ui/markdown-editor/MarkdownEditor.js +0 -116
  159. package/dist/esm/ui/markdown-editor/MarkdownEditor.js.map +0 -1
  160. package/dist/esm/ui/markdown-editor/MarkdownToolbar.js +0 -99
  161. package/dist/esm/ui/markdown-editor/MarkdownToolbar.js.map +0 -1
  162. package/dist/esm/ui/scroll-area.js +0 -62
  163. package/dist/esm/ui/scroll-area.js.map +0 -1
  164. package/dist/esm/ui/select.js +0 -138
  165. package/dist/esm/ui/select.js.map +0 -1
  166. package/dist/esm/ui/textarea.js +0 -19
  167. package/dist/esm/ui/textarea.js.map +0 -1
  168. package/src/components/IconPickerDialog.tsx +0 -136
  169. package/src/components/IconPickerField.tsx +0 -88
  170. package/src/modules/admin-base/components/AdminChat.tsx +0 -122
  171. package/src/modules/admin-base/components/AdminLayout.tsx +0 -111
  172. package/src/modules/admin-base/components/AdminWelcome.tsx +0 -52
  173. package/src/modules/admin-base/context/AdminConfigContext.tsx +0 -36
  174. package/src/modules/admin-base/index.ts +0 -16
  175. package/src/modules/admin-base/types/adminTypes.ts +0 -11
  176. package/src/modules/appCatalog/AppCatalogAdminPage.tsx +0 -274
  177. package/src/modules/approvalMethod/AccessRequestFormFields.tsx +0 -393
  178. package/src/modules/approvalMethod/ApprovalMethodForm.tsx +0 -323
  179. package/src/modules/approvalMethod/ApprovalMethodSelector.tsx +0 -150
  180. package/src/modules/approvalMethod/api/ApiQueryMagazineApprovalMethod.ts +0 -34
  181. package/src/modules/icons/IconManagementPage.tsx +0 -245
  182. package/src/routes/admin/app-for-catalog/$id.tsx +0 -571
  183. package/src/routes/admin/app-for-catalog/index.tsx +0 -19
  184. package/src/routes/admin/app-for-catalog.tsx +0 -12
  185. package/src/routes/admin/approval-methods/index.tsx +0 -161
  186. package/src/routes/admin/approval-methods.tsx +0 -10
  187. package/src/routes/admin/chat.tsx +0 -13
  188. package/src/routes/admin/icons.tsx +0 -22
  189. package/src/routes/admin/index.tsx +0 -9
  190. package/src/routes/admin.tsx +0 -60
@@ -1,323 +0,0 @@
1
- import { useForm } from 'react-hook-form'
2
- import { zodResolver } from '@hookform/resolvers/zod'
3
- import { z } from 'zod'
4
- import { Button } from '~/ui/button'
5
- import {
6
- Form,
7
- FormControl,
8
- FormDescription,
9
- FormField,
10
- FormItem,
11
- FormLabel,
12
- FormMessage,
13
- } from '~/ui/form'
14
- import { Input } from '~/ui/input'
15
- import {
16
- Select,
17
- SelectContent,
18
- SelectItem,
19
- SelectTrigger,
20
- SelectValue,
21
- } from '~/ui/select'
22
- import { EditableListField } from '~/ui/editable-list'
23
-
24
- const APPROVAL_METHOD_TYPES = [
25
- { value: 'service', label: 'Service' },
26
- { value: 'personTeam', label: 'Person/Team' },
27
- { value: 'custom', label: 'Custom' },
28
- ] as const
29
-
30
- // Form schema
31
- const formSchema = z.object({
32
- type: z.enum(['service', 'personTeam', 'custom']),
33
- displayName: z.string().min(1, 'Display name is required'),
34
- // Service config
35
- serviceUrl: z.string().url().optional().or(z.literal('')),
36
- serviceIcon: z.string().optional(),
37
- // PersonTeam config
38
- reachOutContacts: z
39
- .array(
40
- z.object({
41
- displayName: z.string(),
42
- contact: z.string(),
43
- }),
44
- )
45
- .optional(),
46
- })
47
-
48
- type FormData = z.infer<typeof formSchema>
49
-
50
- interface ApprovalMethodFormProps {
51
- mode: 'create' | 'edit'
52
- initialData: {
53
- id?: string
54
- type: 'service' | 'personTeam' | 'custom'
55
- displayName: string
56
- config?: Record<string, unknown>
57
- } | null
58
- onSubmit: (data: any) => void
59
- onCancel: () => void
60
- isPending: boolean
61
- }
62
-
63
- export function ApprovalMethodForm({
64
- mode,
65
- initialData,
66
- onSubmit,
67
- onCancel,
68
- isPending,
69
- }: ApprovalMethodFormProps) {
70
- const config = initialData?.config
71
- const configRecord = config ? config : undefined
72
-
73
- const form = useForm<FormData>({
74
- resolver: zodResolver(formSchema as any) as any,
75
- defaultValues: {
76
- type: initialData?.type || 'custom',
77
- displayName: initialData?.displayName || '',
78
- serviceUrl: configRecord?.url ? String(configRecord.url) : '',
79
- serviceIcon: configRecord?.icon ? String(configRecord.icon) : '',
80
- reachOutContacts: Array.isArray(configRecord?.reachOutContacts)
81
- ? (configRecord.reachOutContacts as Array<{
82
- displayName: string
83
- contact: string
84
- }>)
85
- : [],
86
- },
87
- })
88
-
89
- const typeValue = form.watch('type')
90
-
91
- const handleSubmit = (data: FormData) => {
92
- // Build config based on type
93
- let configPayload: Record<string, unknown> | undefined
94
-
95
- if (data.type === 'service') {
96
- configPayload = {
97
- ...(data.serviceUrl && { url: data.serviceUrl }),
98
- ...(data.serviceIcon && { icon: data.serviceIcon }),
99
- }
100
- } else if (data.type === 'personTeam') {
101
- configPayload = {
102
- ...(data.reachOutContacts?.length && {
103
- reachOutContacts: data.reachOutContacts,
104
- }),
105
- }
106
- }
107
-
108
- const payload = {
109
- ...(mode === 'edit' && initialData?.id && { id: initialData.id }),
110
- type: data.type,
111
- displayName: data.displayName,
112
- config:
113
- Object.keys(configPayload ?? {}).length > 0 ? configPayload : undefined,
114
- }
115
-
116
- onSubmit(payload)
117
- }
118
-
119
- return (
120
- <Form {...form}>
121
- <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
122
- <FormField
123
- control={form.control as any}
124
- name="type"
125
- render={({ field }) => (
126
- <FormItem>
127
- <FormLabel>Type *</FormLabel>
128
- <Select value={field.value} onValueChange={field.onChange}>
129
- <FormControl>
130
- <SelectTrigger>
131
- <SelectValue />
132
- </SelectTrigger>
133
- </FormControl>
134
- <SelectContent>
135
- {APPROVAL_METHOD_TYPES.map((type) => (
136
- <SelectItem key={type.value} value={type.value}>
137
- {type.label}
138
- </SelectItem>
139
- ))}
140
- </SelectContent>
141
- </Select>
142
- <FormDescription>
143
- {typeValue === 'service' &&
144
- 'For bots, ticketing systems, self-service portals'}
145
- {typeValue === 'personTeam' && 'For human approvers or teams'}
146
- {typeValue === 'custom' &&
147
- 'Generic approval method with no specific configuration'}
148
- </FormDescription>
149
- <FormMessage />
150
- </FormItem>
151
- )}
152
- />
153
-
154
- <FormField
155
- control={form.control as any}
156
- name="displayName"
157
- render={({ field }) => (
158
- <FormItem>
159
- <FormLabel>Display Name *</FormLabel>
160
- <FormControl>
161
- <Input placeholder="e.g., Natero Bot, IT Helpdesk" {...field} />
162
- </FormControl>
163
- <FormMessage />
164
- </FormItem>
165
- )}
166
- />
167
-
168
- {/* Service-specific fields */}
169
- {typeValue === 'service' && (
170
- <div className="space-y-4 p-4 border rounded-lg bg-muted/30">
171
- <div className="text-sm font-medium">Service Configuration</div>
172
-
173
- <FormField
174
- control={form.control as any}
175
- name="serviceUrl"
176
- render={({ field }) => (
177
- <FormItem>
178
- <FormLabel>URL</FormLabel>
179
- <FormControl>
180
- <Input type="url" placeholder="https://..." {...field} />
181
- </FormControl>
182
- <FormDescription>
183
- URL to the service (shown as clickable link in app)
184
- </FormDescription>
185
- <FormMessage />
186
- </FormItem>
187
- )}
188
- />
189
-
190
- <FormField
191
- control={form.control as any}
192
- name="serviceIcon"
193
- render={({ field }) => (
194
- <FormItem>
195
- <FormLabel>Icon</FormLabel>
196
- <FormControl>
197
- <Input placeholder="Icon name" {...field} />
198
- </FormControl>
199
- <FormDescription>Optional icon identifier</FormDescription>
200
- <FormMessage />
201
- </FormItem>
202
- )}
203
- />
204
- </div>
205
- )}
206
-
207
- {/* PersonTeam-specific fields */}
208
- {typeValue === 'personTeam' && (
209
- <div className="space-y-4 p-4 border rounded-lg bg-muted/30">
210
- <div className="text-sm font-medium">Reach-Out Contacts</div>
211
- <FormDescription>
212
- People or teams to contact for approval (not necessarily the
213
- approvers themselves)
214
- </FormDescription>
215
-
216
- <FormField
217
- control={form.control as any}
218
- name="reachOutContacts"
219
- render={({ field }) => (
220
- <EditableListField
221
- value={field.value ?? []}
222
- onChange={field.onChange}
223
- columns={[
224
- { accessorKey: 'displayName', header: 'Name' },
225
- { accessorKey: 'contact', header: 'Contact' },
226
- ]}
227
- createEmpty={() => ({ displayName: '', contact: '' })}
228
- getItemKey={(_, i) => String(i)}
229
- addButtonLabel="Add Contact"
230
- emptyMessage="No contacts configured"
231
- renderForm={({ item, onSave, onCancel: onCancelEdit }) => {
232
- if (!item) return null
233
- return (
234
- <ContactForm
235
- item={item}
236
- onSave={onSave}
237
- onCancel={onCancelEdit}
238
- />
239
- )
240
- }}
241
- />
242
- )}
243
- />
244
- </div>
245
- )}
246
-
247
- {/* Custom type - no additional fields */}
248
- {typeValue === 'custom' && (
249
- <div className="p-4 border rounded-lg bg-muted/30 text-sm text-muted-foreground">
250
- Custom type has no additional configuration. App-specific details
251
- will be configured per app.
252
- </div>
253
- )}
254
-
255
- <div className="flex gap-2 pt-4">
256
- <Button type="submit" disabled={isPending}>
257
- {isPending ? 'Saving...' : mode === 'create' ? 'Create' : 'Save'}
258
- </Button>
259
- <Button type="button" variant="outline" onClick={onCancel}>
260
- Cancel
261
- </Button>
262
- </div>
263
- </form>
264
- </Form>
265
- )
266
- }
267
-
268
- // Simple contact form for the dialog
269
- function ContactForm({
270
- item,
271
- onSave,
272
- onCancel: handleCancel,
273
- }: {
274
- item: { displayName: string; contact: string }
275
- onSave: (item: { displayName: string; contact: string }) => void
276
- onCancel: () => void
277
- }) {
278
- const form = useForm<{ displayName: string; contact: string }>({
279
- defaultValues: item,
280
- })
281
-
282
- return (
283
- <form onSubmit={form.handleSubmit(onSave)} className="space-y-4">
284
- <FormField
285
- control={form.control as any}
286
- name="displayName"
287
- render={({ field }) => (
288
- <FormItem>
289
- <FormLabel>Name</FormLabel>
290
- <FormControl>
291
- <Input {...field} placeholder="e.g., John Doe, IT Team" />
292
- </FormControl>
293
- </FormItem>
294
- )}
295
- />
296
- <FormField
297
- control={form.control as any}
298
- name="contact"
299
- render={({ field }) => (
300
- <FormItem>
301
- <FormLabel>Contact</FormLabel>
302
- <FormControl>
303
- <Input {...field} placeholder="e.g., email, Slack handle" />
304
- </FormControl>
305
- </FormItem>
306
- )}
307
- />
308
- <div className="flex gap-2">
309
- <Button type="submit" size="sm">
310
- Save
311
- </Button>
312
- <Button
313
- type="button"
314
- variant="outline"
315
- size="sm"
316
- onClick={handleCancel}
317
- >
318
- Cancel
319
- </Button>
320
- </div>
321
- </form>
322
- )
323
- }
@@ -1,150 +0,0 @@
1
- import { useState } from 'react'
2
- import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
3
- import { Check, ChevronsUpDown, Plus } from 'lucide-react'
4
- import { useTRPC } from '~/api/infra/trpc'
5
- import { cn } from '~/lib/utils'
6
- import { Button } from '~/ui/button'
7
- import {
8
- Command,
9
- CommandEmpty,
10
- CommandGroup,
11
- CommandInput,
12
- CommandItem,
13
- CommandList,
14
- CommandSeparator,
15
- } from '~/ui/command'
16
- import { Popover, PopoverContent, PopoverTrigger } from '~/ui/popover'
17
- import { Dialog, DialogContent, DialogHeader, DialogTitle } from '~/ui/dialog'
18
- import { Badge } from '~/ui/badge'
19
- import { ApprovalMethodForm } from './ApprovalMethodForm'
20
- import { ApiQueryMagazineApprovalMethod } from './api/ApiQueryMagazineApprovalMethod'
21
-
22
- interface ApprovalMethodSelectorProps {
23
- value: string | undefined
24
- onChange: (value: string | undefined) => void
25
- disabled?: boolean
26
- }
27
-
28
- export function ApprovalMethodSelector({
29
- value,
30
- onChange,
31
- disabled = false,
32
- }: ApprovalMethodSelectorProps) {
33
- const [open, setOpen] = useState(false)
34
- const [showQuickAdd, setShowQuickAdd] = useState(false)
35
-
36
- const trpc = useTRPC()
37
- const queryClient = useQueryClient()
38
-
39
- const { data: methods = [] } = useQuery(ApiQueryMagazineApprovalMethod.list())
40
-
41
- const createMutation = useMutation({
42
- ...trpc.approvalMethod.create.mutationOptions(),
43
- onSuccess: (newMethod) => {
44
- queryClient.invalidateQueries({
45
- queryKey: ['approvalMethod'],
46
- })
47
- onChange(newMethod.slug)
48
- setShowQuickAdd(false)
49
- },
50
- })
51
-
52
- const selectedMethod = methods.find((m) => m.slug === value)
53
-
54
- const TYPE_LABELS: Record<string, string> = {
55
- service: 'Service',
56
- personTeam: 'Person/Team',
57
- custom: 'Custom',
58
- }
59
-
60
- return (
61
- <>
62
- <Popover open={open} onOpenChange={setOpen}>
63
- <PopoverTrigger asChild>
64
- <Button
65
- variant="outline"
66
- role="combobox"
67
- aria-expanded={open}
68
- className="w-full justify-between"
69
- disabled={disabled}
70
- >
71
- {selectedMethod ? (
72
- <span className="flex items-center gap-2">
73
- {selectedMethod.displayName}
74
- <Badge variant="secondary" className="text-xs">
75
- {TYPE_LABELS[selectedMethod.type]}
76
- </Badge>
77
- </span>
78
- ) : (
79
- <span className="text-muted-foreground">
80
- Select approval method...
81
- </span>
82
- )}
83
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
84
- </Button>
85
- </PopoverTrigger>
86
- <PopoverContent className="w-[400px] p-0">
87
- <Command>
88
- <CommandInput placeholder="Search methods..." />
89
- <CommandList>
90
- <CommandEmpty>No methods found.</CommandEmpty>
91
- <CommandGroup>
92
- {methods.map((method) => (
93
- <CommandItem
94
- key={method.slug}
95
- value={method.displayName}
96
- onSelect={() => {
97
- onChange(method.slug)
98
- setOpen(false)
99
- }}
100
- >
101
- <Check
102
- className={cn(
103
- 'mr-2 h-4 w-4',
104
- value === method.slug ? 'opacity-100' : 'opacity-0',
105
- )}
106
- />
107
- <span className="flex items-center gap-2">
108
- {method.displayName}
109
- <Badge variant="outline" className="text-xs">
110
- {TYPE_LABELS[method.type]}
111
- </Badge>
112
- </span>
113
- </CommandItem>
114
- ))}
115
- </CommandGroup>
116
- <CommandSeparator />
117
- <CommandGroup>
118
- <CommandItem
119
- onSelect={() => {
120
- setOpen(false)
121
- setShowQuickAdd(true)
122
- }}
123
- >
124
- <Plus className="mr-2 h-4 w-4" />
125
- Create new method...
126
- </CommandItem>
127
- </CommandGroup>
128
- </CommandList>
129
- </Command>
130
- </PopoverContent>
131
- </Popover>
132
-
133
- {/* Quick Add Dialog */}
134
- <Dialog open={showQuickAdd} onOpenChange={setShowQuickAdd}>
135
- <DialogContent>
136
- <DialogHeader>
137
- <DialogTitle>Create Approval Method</DialogTitle>
138
- </DialogHeader>
139
- <ApprovalMethodForm
140
- mode="create"
141
- initialData={null}
142
- onSubmit={(data) => createMutation.mutate(data)}
143
- onCancel={() => setShowQuickAdd(false)}
144
- isPending={createMutation.isPending}
145
- />
146
- </DialogContent>
147
- </Dialog>
148
- </>
149
- )
150
- }
@@ -1,34 +0,0 @@
1
- import { queryOptions } from '@tanstack/react-query'
2
- import { getTrpcFromMeta } from '~/util/reactQueryUtils'
3
-
4
- /**
5
- * Query options for ApprovalMethod data.
6
- *
7
- * Note: We let tRPC infer the return types directly rather than using
8
- * the backend's ApprovalMethod type, because tRPC serializes Date → string
9
- * over JSON. This ensures end-to-end type safety without manual casting.
10
- */
11
- export class ApiQueryMagazineApprovalMethod {
12
- static list() {
13
- return queryOptions({
14
- queryKey: ['approvalMethod', 'list'] as const,
15
- queryFn: (ctx) => getTrpcFromMeta(ctx).approvalMethod.list.query(),
16
- })
17
- }
18
-
19
- static getById(slug: string) {
20
- return queryOptions({
21
- queryKey: ['approvalMethod', 'getById', slug] as const,
22
- queryFn: (ctx) =>
23
- getTrpcFromMeta(ctx).approvalMethod.getById.query({ slug }),
24
- })
25
- }
26
-
27
- static listByType(type: 'service' | 'personTeam' | 'custom') {
28
- return queryOptions({
29
- queryKey: ['approvalMethod', 'listByType', type] as const,
30
- queryFn: (ctx) =>
31
- getTrpcFromMeta(ctx).approvalMethod.listByType.query({ type }),
32
- })
33
- }
34
- }