@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,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
- }