nucleus-core-ts 0.9.830 → 0.9.832
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.
- package/dist/fe/components/IntegrationsPage/components/RunPlanSuggestions.js +80 -51
- package/dist/fe/components/IntegrationsPage/components/runFormat.js +7 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.test.d.ts +1 -0
- package/dist/fe/components/IntegrationsPage/components/runFormat.test.js +47 -0
- package/dist/fe/components/IntegrationsPage/types/records.d.ts +4 -0
- package/package.json +1 -1
|
@@ -51,62 +51,30 @@ const FAMILY_NAME = /^(surname|last|lastname|family|soyad)$/i;
|
|
|
51
51
|
}
|
|
52
52
|
export function RunPlanSuggestions({ plan, mapping, onEditMapping }) {
|
|
53
53
|
const missing = missingRequiredCounts(plan);
|
|
54
|
-
|
|
54
|
+
const withoutAccount = plan.issues.filter((issue)=>issue.kind === 'account_unmatched').length;
|
|
55
|
+
if (missing.length === 0 && withoutAccount === 0) return null;
|
|
55
56
|
const template = suggestEmailTemplate(mapping);
|
|
56
|
-
return /*#__PURE__*/
|
|
57
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
57
58
|
className: "flex flex-col gap-3",
|
|
58
|
-
children:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
title: `${entry.records} record(s) have no ${entry.field}, so they are left out`,
|
|
59
|
+
children: [
|
|
60
|
+
withoutAccount > 0 ? /*#__PURE__*/ _jsxs(Notice, {
|
|
61
|
+
title: `${withoutAccount} record(s) will be imported without a login`,
|
|
62
62
|
tone: "warning",
|
|
63
63
|
children: [
|
|
64
|
-
|
|
64
|
+
/*#__PURE__*/ _jsx("p", {
|
|
65
|
+
children: "They have nothing to create an account under — the column the account is identified by is empty on them. They are still imported, so they appear in the directory; they simply cannot sign in."
|
|
66
|
+
}),
|
|
67
|
+
template ? /*#__PURE__*/ _jsxs("p", {
|
|
68
|
+
className: "mt-1",
|
|
65
69
|
children: [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
/*#__PURE__*/ _jsx("strong", {
|
|
71
|
-
children: "Fallback"
|
|
72
|
-
}),
|
|
73
|
-
" on the ",
|
|
74
|
-
/*#__PURE__*/ _jsx("strong", {
|
|
75
|
-
children: entry.field
|
|
76
|
-
}),
|
|
77
|
-
" row to something like:"
|
|
78
|
-
]
|
|
79
|
-
}),
|
|
80
|
-
/*#__PURE__*/ _jsx("p", {
|
|
81
|
-
className: "mt-1",
|
|
82
|
-
children: /*#__PURE__*/ _jsx("code", {
|
|
83
|
-
className: theme.code,
|
|
84
|
-
children: template
|
|
85
|
-
})
|
|
70
|
+
'Giving that column a Fallback fixes it for all of them at once — ',
|
|
71
|
+
/*#__PURE__*/ _jsx("code", {
|
|
72
|
+
className: theme.code,
|
|
73
|
+
children: template
|
|
86
74
|
}),
|
|
87
|
-
|
|
88
|
-
className: "mt-1",
|
|
89
|
-
children: [
|
|
90
|
-
"Pair it with the ",
|
|
91
|
-
/*#__PURE__*/ _jsx("strong", {
|
|
92
|
-
children: "E-mail safe"
|
|
93
|
-
}),
|
|
94
|
-
" transform and ",
|
|
95
|
-
/*#__PURE__*/ _jsx("em", {
|
|
96
|
-
children: "Ayşe Yılmaz"
|
|
97
|
-
}),
|
|
98
|
-
' ',
|
|
99
|
-
"becomes ",
|
|
100
|
-
/*#__PURE__*/ _jsx("em", {
|
|
101
|
-
children: "ayse.yilmaz@example.com"
|
|
102
|
-
}),
|
|
103
|
-
". Records that already carry an address keep the one they have — the fallback is only used when the source is empty."
|
|
104
|
-
]
|
|
105
|
-
})
|
|
75
|
+
' — and everybody gets an account.'
|
|
106
76
|
]
|
|
107
|
-
}) :
|
|
108
|
-
children: `Nothing in this import fills ${entry.field}, and the table will not accept a row without it. Either map a source field to it, or give that row a Fallback — a fixed value, or one built from other fields with {braces}.`
|
|
109
|
-
}),
|
|
77
|
+
}) : null,
|
|
110
78
|
onEditMapping ? /*#__PURE__*/ _jsx("button", {
|
|
111
79
|
className: `${theme.button.secondary} mt-3`,
|
|
112
80
|
onClick: onEditMapping,
|
|
@@ -114,7 +82,68 @@ export function RunPlanSuggestions({ plan, mapping, onEditMapping }) {
|
|
|
114
82
|
children: "Open the mapping"
|
|
115
83
|
}) : null
|
|
116
84
|
]
|
|
117
|
-
},
|
|
118
|
-
|
|
85
|
+
}) : null,
|
|
86
|
+
missing.map((entry)=>{
|
|
87
|
+
const isEmail = EMAIL_LIKE.test(entry.field);
|
|
88
|
+
return /*#__PURE__*/ _jsxs(Notice, {
|
|
89
|
+
title: `${entry.records} record(s) have no ${entry.field}, so they are left out`,
|
|
90
|
+
tone: "warning",
|
|
91
|
+
children: [
|
|
92
|
+
isEmail && template ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
93
|
+
children: [
|
|
94
|
+
/*#__PURE__*/ _jsxs("p", {
|
|
95
|
+
children: [
|
|
96
|
+
"An address can be built from the fields the source DOES send. On the mapping, set",
|
|
97
|
+
' ',
|
|
98
|
+
/*#__PURE__*/ _jsx("strong", {
|
|
99
|
+
children: "Fallback"
|
|
100
|
+
}),
|
|
101
|
+
" on the ",
|
|
102
|
+
/*#__PURE__*/ _jsx("strong", {
|
|
103
|
+
children: entry.field
|
|
104
|
+
}),
|
|
105
|
+
" row to something like:"
|
|
106
|
+
]
|
|
107
|
+
}),
|
|
108
|
+
/*#__PURE__*/ _jsx("p", {
|
|
109
|
+
className: "mt-1",
|
|
110
|
+
children: /*#__PURE__*/ _jsx("code", {
|
|
111
|
+
className: theme.code,
|
|
112
|
+
children: template
|
|
113
|
+
})
|
|
114
|
+
}),
|
|
115
|
+
/*#__PURE__*/ _jsxs("p", {
|
|
116
|
+
className: "mt-1",
|
|
117
|
+
children: [
|
|
118
|
+
"Pair it with the ",
|
|
119
|
+
/*#__PURE__*/ _jsx("strong", {
|
|
120
|
+
children: "E-mail safe"
|
|
121
|
+
}),
|
|
122
|
+
" transform and ",
|
|
123
|
+
/*#__PURE__*/ _jsx("em", {
|
|
124
|
+
children: "Ayşe Yılmaz"
|
|
125
|
+
}),
|
|
126
|
+
' ',
|
|
127
|
+
"becomes ",
|
|
128
|
+
/*#__PURE__*/ _jsx("em", {
|
|
129
|
+
children: "ayse.yilmaz@example.com"
|
|
130
|
+
}),
|
|
131
|
+
". Records that already carry an address keep the one they have — the fallback is only used when the source is empty."
|
|
132
|
+
]
|
|
133
|
+
})
|
|
134
|
+
]
|
|
135
|
+
}) : /*#__PURE__*/ _jsx("p", {
|
|
136
|
+
children: `Nothing in this import fills ${entry.field}, and the table will not accept a row without it. Either map a source field to it, or give that row a Fallback — a fixed value, or one built from other fields with {braces}.`
|
|
137
|
+
}),
|
|
138
|
+
onEditMapping ? /*#__PURE__*/ _jsx("button", {
|
|
139
|
+
className: `${theme.button.secondary} mt-3`,
|
|
140
|
+
onClick: onEditMapping,
|
|
141
|
+
type: "button",
|
|
142
|
+
children: "Open the mapping"
|
|
143
|
+
}) : null
|
|
144
|
+
]
|
|
145
|
+
}, entry.field);
|
|
146
|
+
})
|
|
147
|
+
]
|
|
119
148
|
});
|
|
120
149
|
}
|
|
@@ -53,6 +53,13 @@ export function formatDuration(startedAt, finishedAt) {
|
|
|
53
53
|
`${run.updatedCount} updated`,
|
|
54
54
|
`${run.skippedCount} skipped`
|
|
55
55
|
];
|
|
56
|
+
// An import that also opened 877 logins did the largest thing it did that day
|
|
57
|
+
// and said nothing about it — the history read exactly like one that opened
|
|
58
|
+
// none, and the only way to tell was to go and count the users table.
|
|
59
|
+
if ((run.accountsCreatedCount ?? 0) > 0) parts.push(`${run.accountsCreatedCount} login(s)`);
|
|
60
|
+
if ((run.referencesCreatedCount ?? 0) > 0) {
|
|
61
|
+
parts.push(`${run.referencesCreatedCount} value(s) added elsewhere`);
|
|
62
|
+
}
|
|
56
63
|
if (run.deletedCount > 0) parts.push(`${run.deletedCount} deleted`);
|
|
57
64
|
if (run.deactivatedCount > 0) parts.push(`${run.deactivatedCount} deactivated`);
|
|
58
65
|
if (run.errorCount > 0) parts.push(`${run.errorCount} failed`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { countersLine } from './runFormat';
|
|
3
|
+
/**
|
|
4
|
+
* An import that also opened 877 logins did the largest thing it did that day.
|
|
5
|
+
* The history line read exactly like one that opened none, and the only way to
|
|
6
|
+
* tell them apart was to go and count the users table.
|
|
7
|
+
*/ const run = (over)=>({
|
|
8
|
+
createdCount: 0,
|
|
9
|
+
updatedCount: 0,
|
|
10
|
+
skippedCount: 0,
|
|
11
|
+
deletedCount: 0,
|
|
12
|
+
deactivatedCount: 0,
|
|
13
|
+
errorCount: 0,
|
|
14
|
+
...over
|
|
15
|
+
});
|
|
16
|
+
describe('the counts of a finished run', ()=>{
|
|
17
|
+
it('always says what it created, updated and skipped', ()=>{
|
|
18
|
+
expect(countersLine(run({
|
|
19
|
+
createdCount: 3,
|
|
20
|
+
updatedCount: 2,
|
|
21
|
+
skippedCount: 1
|
|
22
|
+
}))).toBe('3 created · 2 updated · 1 skipped');
|
|
23
|
+
});
|
|
24
|
+
it('says how many logins it opened', ()=>{
|
|
25
|
+
expect(countersLine(run({
|
|
26
|
+
createdCount: 880,
|
|
27
|
+
accountsCreatedCount: 877
|
|
28
|
+
}))).toContain('877 login(s)');
|
|
29
|
+
});
|
|
30
|
+
it('says what it added to other tables', ()=>{
|
|
31
|
+
expect(countersLine(run({
|
|
32
|
+
referencesCreatedCount: 2
|
|
33
|
+
}))).toContain('2 value(s) added elsewhere');
|
|
34
|
+
});
|
|
35
|
+
it('stays quiet about the kinds that did not happen', ()=>{
|
|
36
|
+
// A line of zeros is a line nobody reads.
|
|
37
|
+
const line = countersLine(run({
|
|
38
|
+
createdCount: 5
|
|
39
|
+
}));
|
|
40
|
+
expect(line).not.toContain('login');
|
|
41
|
+
expect(line).not.toContain('deleted');
|
|
42
|
+
expect(line).not.toContain('failed');
|
|
43
|
+
});
|
|
44
|
+
it('reads an older run that never recorded these at all', ()=>{
|
|
45
|
+
expect(()=>countersLine(run({}))).not.toThrow();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -180,6 +180,10 @@ export type IntegrationRun = {
|
|
|
180
180
|
totalFetched: number;
|
|
181
181
|
sourceReportedTotal?: number | null;
|
|
182
182
|
createdCount: number;
|
|
183
|
+
/** Logins this run opened so its records could be signed in to. */
|
|
184
|
+
accountsCreatedCount?: number;
|
|
185
|
+
/** Rows it added to OTHER tables so its records had something to point at. */
|
|
186
|
+
referencesCreatedCount?: number;
|
|
183
187
|
updatedCount: number;
|
|
184
188
|
skippedCount: number;
|
|
185
189
|
deletedCount: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nucleus-core-ts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.832",
|
|
4
4
|
"description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
|
|
5
5
|
"author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|