@kronor/dtv 0.2.9 → 0.3.1

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 (102) hide show
  1. package/dist/assets/InterVariable-CWi-zmRD.woff2 +0 -0
  2. package/dist/assets/InterVariable-Italic-d6KXgdvN.woff2 +0 -0
  3. package/dist/assets/index-CKkrV_Rb.css +1 -0
  4. package/dist/assets/index-DMT2lDJd.js +2105 -0
  5. package/dist/assets/primeicons-C6QP2o4f.woff2 +0 -0
  6. package/dist/assets/primeicons-DMOk5skT.eot +0 -0
  7. package/dist/assets/primeicons-Dr5RGzOO.svg +345 -0
  8. package/dist/assets/primeicons-MpK4pl85.ttf +0 -0
  9. package/dist/assets/primeicons-WjwUDZjB.woff +0 -0
  10. package/dist/index.html +17 -0
  11. package/package.json +5 -1
  12. package/.editorconfig +0 -12
  13. package/.github/copilot-instructions.md +0 -64
  14. package/.github/workflows/ci.yml +0 -51
  15. package/.husky/pre-commit +0 -8
  16. package/e2e/app.spec.ts +0 -6
  17. package/e2e/cell-renderer-setfilterstate.spec.ts +0 -63
  18. package/e2e/filter-sharing.spec.ts +0 -113
  19. package/e2e/filter-url-persistence.spec.ts +0 -36
  20. package/e2e/graphqlMock.ts +0 -144
  21. package/e2e/multi-field-filters.spec.ts +0 -95
  22. package/e2e/pagination.spec.ts +0 -38
  23. package/e2e/payment-request-email-filter.spec.ts +0 -67
  24. package/e2e/save-filter-splitbutton.spec.ts +0 -68
  25. package/e2e/simple-view-email-filter.spec.ts +0 -67
  26. package/e2e/simple-view-transforms.spec.ts +0 -171
  27. package/e2e/simple-view.spec.ts +0 -104
  28. package/e2e/transform-regression.spec.ts +0 -108
  29. package/eslint.config.js +0 -30
  30. package/index.html +0 -17
  31. package/jest.config.js +0 -10
  32. package/playwright.config.ts +0 -54
  33. package/src/App.externalRuntime.test.ts +0 -190
  34. package/src/App.tsx +0 -540
  35. package/src/assets/react.svg +0 -1
  36. package/src/components/AIAssistantForm.tsx +0 -241
  37. package/src/components/FilterForm.test.ts +0 -82
  38. package/src/components/FilterForm.tsx +0 -375
  39. package/src/components/PhoneNumberFilter.tsx +0 -102
  40. package/src/components/SavedFilterList.tsx +0 -181
  41. package/src/components/SpeechInput.tsx +0 -67
  42. package/src/components/Table.tsx +0 -119
  43. package/src/components/TablePagination.tsx +0 -40
  44. package/src/components/aiAssistant.test.ts +0 -270
  45. package/src/components/aiAssistant.ts +0 -291
  46. package/src/framework/cell-renderer-components/CurrencyAmount.tsx +0 -30
  47. package/src/framework/cell-renderer-components/LayoutHelpers.tsx +0 -74
  48. package/src/framework/cell-renderer-components/Link.tsx +0 -28
  49. package/src/framework/cell-renderer-components/Mapping.tsx +0 -11
  50. package/src/framework/cell-renderer-components.test.ts +0 -353
  51. package/src/framework/column-definition.tsx +0 -85
  52. package/src/framework/currency.test.ts +0 -46
  53. package/src/framework/currency.ts +0 -62
  54. package/src/framework/data.staticConditions.test.ts +0 -46
  55. package/src/framework/data.test.ts +0 -167
  56. package/src/framework/data.ts +0 -162
  57. package/src/framework/filter-form-state.test.ts +0 -189
  58. package/src/framework/filter-form-state.ts +0 -185
  59. package/src/framework/filter-sharing.test.ts +0 -135
  60. package/src/framework/filter-sharing.ts +0 -118
  61. package/src/framework/filters.ts +0 -194
  62. package/src/framework/graphql.buildHasuraConditions.test.ts +0 -473
  63. package/src/framework/graphql.paginationKey.test.ts +0 -29
  64. package/src/framework/graphql.test.ts +0 -286
  65. package/src/framework/graphql.ts +0 -462
  66. package/src/framework/native-runtime/index.tsx +0 -33
  67. package/src/framework/native-runtime/nativeComponents.test.ts +0 -108
  68. package/src/framework/runtime-reference.test.ts +0 -172
  69. package/src/framework/runtime.ts +0 -15
  70. package/src/framework/saved-filters.test.ts +0 -422
  71. package/src/framework/saved-filters.ts +0 -293
  72. package/src/framework/state.test.ts +0 -86
  73. package/src/framework/state.ts +0 -148
  74. package/src/framework/transform.test.ts +0 -51
  75. package/src/framework/view-parser-initialvalues.test.ts +0 -228
  76. package/src/framework/view-parser.ts +0 -714
  77. package/src/framework/view.test.ts +0 -1805
  78. package/src/framework/view.ts +0 -38
  79. package/src/index.css +0 -6
  80. package/src/main.tsx +0 -99
  81. package/src/views/index.ts +0 -12
  82. package/src/views/payment-requests/components/NoRowsExtendDateRange.tsx +0 -37
  83. package/src/views/payment-requests/components/PaymentMethod.tsx +0 -184
  84. package/src/views/payment-requests/components/PaymentStatusTag.tsx +0 -61
  85. package/src/views/payment-requests/index.ts +0 -1
  86. package/src/views/payment-requests/runtime.tsx +0 -145
  87. package/src/views/payment-requests/view.json +0 -692
  88. package/src/views/payment-requests-initial-values.test.ts +0 -73
  89. package/src/views/request-log/index.ts +0 -2
  90. package/src/views/request-log/runtime.tsx +0 -47
  91. package/src/views/request-log/view.json +0 -123
  92. package/src/views/simple-test-view/index.ts +0 -3
  93. package/src/views/simple-test-view/runtime.tsx +0 -85
  94. package/src/views/simple-test-view/view.json +0 -191
  95. package/src/vite-env.d.ts +0 -1
  96. package/tailwind.config.js +0 -7
  97. package/tsconfig.app.json +0 -26
  98. package/tsconfig.jest.json +0 -6
  99. package/tsconfig.json +0 -7
  100. package/tsconfig.node.json +0 -24
  101. package/vite.config.ts +0 -11
  102. /package/{public → dist}/vite.svg +0 -0
@@ -1,241 +0,0 @@
1
- import { Button } from 'primereact/button';
2
- import { Toast } from 'primereact/toast';
3
- import SpeechInput from './SpeechInput';
4
- import { useState, RefObject } from 'react';
5
- import { FilterSchemasAndGroups } from '../framework/filters';
6
- import { View } from '../framework/view';
7
- import { generateFilterWithAI, GeminiApi } from './aiAssistant';
8
- import { FilterState } from '../framework/state';
9
-
10
- interface AIAssistantFormProps {
11
- filterSchema: FilterSchemasAndGroups;
12
- filterState: FilterState;
13
- setFilterSchema: (schema: FilterSchemasAndGroups) => void;
14
- setFilterState: (state: FilterState) => void;
15
- selectedView: View;
16
- geminiApiKey: string;
17
- toast: RefObject<Toast | null>;
18
- // When AI applies filters, ensure the filter form becomes visible so user can inspect/edit them
19
- setShowFilterForm: (value: boolean | ((prev: boolean) => boolean)) => void;
20
- }
21
-
22
- export default function AIAssistantForm({
23
- setFilterState,
24
- selectedView,
25
- geminiApiKey,
26
- toast,
27
- setShowFilterForm
28
- }: AIAssistantFormProps) {
29
- const [aiPrompt, setAiPrompt] = useState('authorized payments in euro or danish krona in the first week of april 2025');
30
- // const [aiFilterExprInput, setAiFilterExprInput] = useState('(payment method or currency) and a filter to exclude payment status');
31
- const [aiLoading, setAiLoading] = useState(false);
32
- return (
33
- <div className="tw:flex tw:flex-col tw:gap-2 tw:mb-3">
34
- <label className="tw:text-sm tw:font-semibold tw:mb-1" htmlFor="ai-prompt">AI Prompt</label>
35
- <div className="tw:flex tw:gap-2">
36
- <SpeechInput value={aiPrompt} onChange={setAiPrompt} />
37
- <Button
38
- type="button"
39
- outlined
40
- label="Update filters"
41
- icon='pi pi-sparkles'
42
- loading={aiLoading}
43
- onClick={async () => {
44
- setAiLoading(true);
45
- try {
46
- await generateFilterWithAI(selectedView.filterSchema, aiPrompt, setFilterState, GeminiApi, geminiApiKey, toast);
47
- // Reveal filter form so user sees applied changes
48
- setShowFilterForm(true);
49
-
50
- toast.current?.show({
51
- severity: 'success',
52
- summary: 'AI Filter Generated',
53
- detail: 'Filter values have been populated based on your prompt',
54
- life: 3000
55
- });
56
- } catch (error) {
57
- console.error('AI filter generation failed:', error);
58
- toast.current?.show({
59
- severity: 'error',
60
- summary: 'AI Generation Failed',
61
- detail: 'Failed to generate filter from AI. Please try again.',
62
- life: 3000
63
- });
64
- } finally {
65
- setAiLoading(false);
66
- }
67
- }}
68
- className='p-button-secondary'
69
- />
70
- </div>
71
- {/* <label className="tw:text-sm tw:font-semibold tw:mt-4" htmlFor="ai-filterexpr-input">Add a custom filter with AI</label>
72
- <div className="tw:flex tw:items-center tw:gap-2">
73
- <InputText
74
- id="ai-filterexpr-input"
75
- value={aiFilterExprInput}
76
- onChange={e => setAiFilterExprInput(e.target.value)}
77
- placeholder="Describe filter in natural language..."
78
- className="tw:flex-1"
79
- />
80
- <Button
81
- type="button"
82
- outlined
83
- label="Generate filter"
84
- icon='pi pi-sparkles'
85
- loading={aiLoading}
86
- onClick={async () => {
87
- setAiLoading(true);
88
- try {
89
- const filterSchemaJson = JSON.stringify(filterSchema, null, 2);
90
- const allKeys = Array.from(
91
- new Set(
92
- selectedView.filterSchema.filters
93
- .flatMap(filter => getFieldNodes(filter.expression)
94
- .map(node => node.field)
95
- )
96
- )
97
- );
98
- const filterControlType = [
99
- 'type FilterControl =',
100
- ' | { type: "text"; label?: string; placeholder?: string }',
101
- ' | { type: "number"; label?: string; placeholder?: string }',
102
- ' | { type: "date"; label?: string; placeholder?: string }',
103
- ' | { type: "dropdown"; label?: string; items: { label: string; value: any }[] }',
104
- ' | { type: "multiselect"; label?: string; items: { label: string; value: any }[], filterable?: boolean }',
105
- ' | { type: "customOperator"; label?: string; operators: { label: string; value: string }[]; valueControl: FilterControl }',
106
- ' | { type: "custom"; component: React.ComponentType<any>; props?: Record<string, any>; label?: string };'
107
- ].join('\n');
108
- const filterExprType = [
109
- 'type FilterExpr =',
110
- ' | { type: "equals"; field: string; value: FilterControl }',
111
- ' | { type: "notEquals"; field: string; value: FilterControl }',
112
- ' | { type: "greaterThan"; field: string; value: FilterControl }',
113
- ' | { type: "lessThan"; field: string; value: FilterControl }',
114
- ' | { type: "greaterThanOrEqual"; field: string; value: FilterControl }',
115
- ' | { type: "lessThanOrEqual"; field: string; value: FilterControl }',
116
- ' | { type: "in"; field: string; value: FilterControl }',
117
- ' | { type: "notIn"; field: string; value: FilterControl }',
118
- ' | { type: "like"; field: string; value: FilterControl }',
119
- ' | { type: "iLike"; field: string; value: FilterControl }',
120
- ' | { type: "isNull"; field: string; value: FilterControl }',
121
- ' | { type: "and"; filters: FilterExpr[] }',
122
- ' | { type: "or"; filters: FilterExpr[] }',
123
- ' | { type: "not"; filter: FilterExpr };'
124
- ].join('\n');
125
- const template = [
126
- 'You are an expert TypeScript assistant.',
127
- '',
128
- 'Here are the type definitions for FilterControl and FilterExpr:',
129
- '',
130
- filterControlType,
131
- '',
132
- filterExprType,
133
- '',
134
- 'Available data keys:',
135
- JSON.stringify(allKeys, null, 2),
136
- '',
137
- 'Current filter schema (including control configuration, dropdown/multiselect values, etc.):',
138
- filterSchemaJson,
139
- '',
140
- 'User prompt:',
141
- aiFilterExprInput,
142
- '',
143
- 'Instructions:',
144
- '- Generate a valid FilterExpr as JSON, using only the available data keys.',
145
- '- When generating a filter for a field, use the control configuration from the filter schema (e.g. use the same dropdown/multiselect values for matching data keys).',
146
- '- Only use supported FilterControl types (text, number, date, dropdown, multiselect) with labels',
147
- '- Do not use custom or transformation functions.',
148
- '- Output only the JSON for the FilterExpr, nothing else.',
149
- '- The JSON must be valid and parseable.',
150
- ].join('\n');
151
- let aiContent = '';
152
- try {
153
- const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${geminiApiKey}`, {
154
- method: 'POST',
155
- headers: { 'Content-Type': 'application/json' },
156
- body: JSON.stringify({
157
- contents: [{ role: 'user', parts: [{ text: template }] }]
158
- })
159
- });
160
- if (!response.ok) throw new Error('Gemini API error');
161
- const data = await response.json();
162
- aiContent = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
163
- } catch (error) {
164
- console.error('Gemini API error:', error);
165
- toast.current?.show({
166
- severity: 'error',
167
- summary: 'API Error',
168
- detail: 'Failed to get response from Gemini API.',
169
- life: 3000
170
- });
171
- setAiLoading(false);
172
- return;
173
- }
174
- let exprJson: any = null;
175
- const match = aiContent.match(/\{[\s\S]*\}|\[[\s\S]*\]/);
176
- if (match) {
177
- try {
178
- exprJson = JSON.parse(match[0]);
179
- } catch (error) {
180
- console.error('Invalid JSON from AI:', error);
181
- toast.current?.show({
182
- severity: 'warn',
183
- summary: 'Invalid Response',
184
- detail: 'AI response is not valid JSON',
185
- life: 3000
186
- });
187
- setAiLoading(false);
188
- return;
189
- }
190
- } else {
191
- toast.current?.show({
192
- severity: 'warn',
193
- summary: 'No Filter Found',
194
- detail: 'Could not find JSON in AI response',
195
- life: 3000
196
- });
197
- setAiLoading(false);
198
- return;
199
- }
200
- const filterExpr = filterExprFromJSON(exprJson);
201
- if (!filterExpr) {
202
- toast.current?.show({
203
- severity: 'warn',
204
- summary: 'Parse Error',
205
- detail: 'Could not parse AI response as FilterExpr',
206
- life: 3000
207
- });
208
- setAiLoading(false);
209
- return;
210
- }
211
- // Generate a unique ID for the AI filter
212
- const aiFilterId = `ai-filter-${Date.now()}`;
213
- const newSchema = {
214
- ...filterSchema,
215
- filters: [
216
- ...filterSchema.filters,
217
- { id: aiFilterId, label: 'AI Filter', expression: filterExpr, group: 'default', aiGenerated: true }
218
- ]
219
- };
220
- setFilterSchema(newSchema);
221
-
222
- // Add the AI-generated filter to the existing FilterState Map
223
- filterState.set(aiFilterId, buildInitialFormState(filterExpr));
224
- setFilterState(new Map(filterState)); // Trigger React re-render
225
-
226
- toast.current?.show({
227
- severity: 'success',
228
- summary: 'AI Filter Added',
229
- detail: 'New filter has been created and added to the form',
230
- life: 3000
231
- });
232
- } finally {
233
- setAiLoading(false);
234
- }
235
- }}
236
- className='p-button-secondary'
237
- />
238
- </div> */}
239
- </div>
240
- );
241
- }
@@ -1,82 +0,0 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
- import { describe, it, expect } from '@jest/globals';
5
- import { buildInitialFormState, FormStateInitMode } from '../framework/state';
6
- import { FilterExpr } from '../framework/filters';
7
-
8
- describe('FilterForm state builders', () => {
9
- describe('buildInitialFormState vs buildEmptyFormState', () => {
10
- it('buildInitialFormState uses initialValue when available', () => {
11
- const expr: FilterExpr = {
12
- type: 'equals',
13
- field: 'test',
14
- value: {
15
- type: 'text',
16
- initialValue: 'default-value'
17
- }
18
- };
19
-
20
- const initialState = buildInitialFormState(expr);
21
- expect(initialState).toEqual({
22
- type: 'leaf',
23
- value: 'default-value'
24
- });
25
- });
26
-
27
- it('both functions handle expressions without initialValue the same way', () => {
28
- const expr: FilterExpr = {
29
- type: 'equals',
30
- field: 'test',
31
- value: {
32
- type: 'text'
33
- }
34
- };
35
-
36
- const initialState = buildInitialFormState(expr);
37
- const emptyState = buildInitialFormState(expr, FormStateInitMode.Empty);
38
-
39
- // Both should have empty string value when no initialValue is provided
40
- expect((initialState as any).value).toBe('');
41
- expect((emptyState as any).value).toBe('');
42
- });
43
-
44
- it('buildInitialFormState with mode=Empty handles complex nested expressions', () => {
45
- const expr: FilterExpr = {
46
- type: 'and',
47
- filters: [
48
- {
49
- type: 'equals',
50
- field: 'field1',
51
- value: {
52
- type: 'text',
53
- initialValue: 'value1'
54
- }
55
- },
56
- {
57
- type: 'not',
58
- filter: {
59
- type: 'equals',
60
- field: 'field2',
61
- value: {
62
- type: 'text',
63
- initialValue: 'value2'
64
- }
65
- }
66
- }
67
- ]
68
- };
69
-
70
- const emptyState = buildInitialFormState(expr, FormStateInitMode.Empty);
71
- expect(emptyState.type).toBe('and');
72
- expect((emptyState as any).children).toHaveLength(2);
73
-
74
- // First child should be empty
75
- expect((emptyState as any).children[0].value).toBe('');
76
-
77
- // Second child is a NOT expression, check its nested child
78
- expect((emptyState as any).children[1].type).toBe('not');
79
- expect((emptyState as any).children[1].child.value).toBe('');
80
- });
81
- });
82
- });