@geniee-inc/sfa-kit 1.6.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.
- package/GUIDE.md +585 -0
- package/LICENSE.md +21 -0
- package/README.md +31 -0
- package/package.json +36 -0
- package/src/index.d.ts +29 -0
- package/src/index.js +21 -0
- package/src/lib/auth/DevToolbar.d.ts +38 -0
- package/src/lib/auth/DevToolbar.js +271 -0
- package/src/lib/auth/SfaLoginGate.d.ts +53 -0
- package/src/lib/auth/SfaLoginGate.js +462 -0
- package/src/lib/auth/StandalonePreviewContext.d.ts +27 -0
- package/src/lib/auth/StandalonePreviewContext.js +14 -0
- package/src/lib/auth/embed-protocol.d.ts +40 -0
- package/src/lib/auth/embed-protocol.js +56 -0
- package/src/lib/auth/exchange-code.d.ts +14 -0
- package/src/lib/auth/exchange-code.js +45 -0
- package/src/lib/auth/keystone-auth.d.ts +16 -0
- package/src/lib/auth/keystone-auth.js +84 -0
- package/src/lib/auth/session-store.d.ts +19 -0
- package/src/lib/auth/session-store.js +73 -0
- package/src/lib/auth/types.d.ts +33 -0
- package/src/lib/auth/types.js +1 -0
- package/src/lib/auth/useEmbedContext.d.ts +22 -0
- package/src/lib/auth/useEmbedContext.js +31 -0
- package/src/lib/components/EmptyState.d.ts +18 -0
- package/src/lib/components/EmptyState.js +36 -0
- package/src/lib/components/ErrorBoundary.d.ts +31 -0
- package/src/lib/components/ErrorBoundary.js +100 -0
- package/src/lib/components/FieldRenderer.d.ts +11 -0
- package/src/lib/components/FieldRenderer.js +57 -0
- package/src/lib/components/RecordList.d.ts +20 -0
- package/src/lib/components/RecordList.js +139 -0
- package/src/lib/components/RecordView.d.ts +14 -0
- package/src/lib/components/RecordView.js +153 -0
- package/src/lib/errors.d.ts +15 -0
- package/src/lib/errors.js +15 -0
- package/src/lib/hooks/useCreateRecord.d.ts +17 -0
- package/src/lib/hooks/useCreateRecord.js +48 -0
- package/src/lib/hooks/useDeleteRecord.d.ts +16 -0
- package/src/lib/hooks/useDeleteRecord.js +48 -0
- package/src/lib/hooks/useDescribe.d.ts +16 -0
- package/src/lib/hooks/useDescribe.js +40 -0
- package/src/lib/hooks/useFieldTypes.d.ts +15 -0
- package/src/lib/hooks/useFieldTypes.js +39 -0
- package/src/lib/hooks/useObjectCatalog.d.ts +15 -0
- package/src/lib/hooks/useObjectCatalog.js +39 -0
- package/src/lib/hooks/useRecord.d.ts +15 -0
- package/src/lib/hooks/useRecord.js +49 -0
- package/src/lib/hooks/useRecordList.d.ts +19 -0
- package/src/lib/hooks/useRecordList.js +82 -0
- package/src/lib/hooks/useRelatedRecords.d.ts +65 -0
- package/src/lib/hooks/useRelatedRecords.js +227 -0
- package/src/lib/hooks/useUpdateRecord.d.ts +23 -0
- package/src/lib/hooks/useUpdateRecord.js +55 -0
- package/src/lib/provider/SfaClient.d.ts +98 -0
- package/src/lib/provider/SfaClient.js +688 -0
- package/src/lib/provider/SfaProvider.d.ts +38 -0
- package/src/lib/provider/SfaProvider.js +44 -0
- package/src/lib/provider/types.d.ts +184 -0
- package/src/lib/provider/types.js +1 -0
- package/src/lib/provider/useSfaClient.d.ts +11 -0
- package/src/lib/provider/useSfaClient.js +24 -0
- package/src/lib/theme.d.ts +55 -0
- package/src/lib/theme.js +68 -0
- package/src/lib/version.d.ts +1 -0
- package/src/lib/version.js +1 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
3
|
+
import { useRecordList } from '../hooks/useRecordList';
|
|
4
|
+
import { color, font, radius, space, baseContainerStyle, baseButtonStyle, } from '../theme';
|
|
5
|
+
import { SFA_KIT_VERSION } from '../version';
|
|
6
|
+
import { FieldRenderer } from './FieldRenderer';
|
|
7
|
+
const MAX_AUTO_COLUMNS = 8;
|
|
8
|
+
/**
|
|
9
|
+
* @description Fetches and displays a paginated list of SFA/CRM records as a table.
|
|
10
|
+
*/
|
|
11
|
+
export function RecordList({ collection, columns, params, pageSize = 20, showPagination = true, onRowClick, onError, }) {
|
|
12
|
+
const listParams = useMemo(() => (Object.assign(Object.assign({}, params), { limit: pageSize })), [params, pageSize]);
|
|
13
|
+
const { items, totalCount, hasMore, pageIndex, isLoading, error, goToPage } = useRecordList(collection, listParams);
|
|
14
|
+
// Use a ref so inline onError callbacks don't re-fire the effect on
|
|
15
|
+
// every parent render when error is present.
|
|
16
|
+
const onErrorRef = useRef(onError);
|
|
17
|
+
onErrorRef.current = onError;
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
var _a;
|
|
20
|
+
if (error)
|
|
21
|
+
(_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, error);
|
|
22
|
+
}, [error]);
|
|
23
|
+
if (error) {
|
|
24
|
+
return (_jsxs("div", { style: styles.container, children: [_jsx("div", { style: styles.error, children: error.message }), _jsxs("div", { style: styles.versionFooter, children: ["@geniee-inc/sfa-kit v", SFA_KIT_VERSION] })] }));
|
|
25
|
+
}
|
|
26
|
+
const resolvedColumns = columns !== null && columns !== void 0 ? columns : autoDetectColumns(items);
|
|
27
|
+
const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
|
|
28
|
+
const rangeStart = pageIndex * pageSize + 1;
|
|
29
|
+
const rangeEnd = Math.min((pageIndex + 1) * pageSize, totalCount);
|
|
30
|
+
return (_jsxs("div", { style: styles.container, children: [_jsxs("div", { style: styles.header, children: [_jsx("span", { style: styles.collectionName, children: collection }), !isLoading && _jsxs("span", { style: styles.count, children: [totalCount, " records"] })] }), _jsx("div", { style: styles.tableWrapper, children: _jsxs("table", { style: styles.table, children: [_jsx("thead", { children: _jsx("tr", { children: resolvedColumns.map(col => {
|
|
31
|
+
var _a;
|
|
32
|
+
return (_jsx("th", { style: Object.assign(Object.assign({}, styles.th), { width: col.width }), children: (_a = col.header) !== null && _a !== void 0 ? _a : col.fieldName }, col.fieldName));
|
|
33
|
+
}) }) }), _jsxs("tbody", { children: [isLoading && (_jsx("tr", { children: _jsx("td", { colSpan: resolvedColumns.length, style: styles.loadingCell, children: "Loading..." }) })), !isLoading && items.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: resolvedColumns.length, style: styles.emptyCell, children: "No records found" }) })), !isLoading &&
|
|
34
|
+
items.length > 0 &&
|
|
35
|
+
items.map(record => (_jsx("tr", { style: onRowClick ? styles.clickableRow : styles.row, onClick: onRowClick ? () => onRowClick(record) : undefined, children: resolvedColumns.map(col => {
|
|
36
|
+
const fieldValue = record.fields[col.fieldName];
|
|
37
|
+
return (_jsx("td", { style: styles.td, children: fieldValue ? (_jsx(FieldRenderer, { fieldValue: fieldValue, fieldName: col.fieldName })) : (_jsx("span", { style: styles.emptyValue, children: "\u2014" })) }, col.fieldName));
|
|
38
|
+
}) }, record.record_id)))] })] }) }), showPagination && totalCount > 0 && (_jsxs("div", { style: styles.pagination, children: [_jsx("button", { style: styles.pageButton, disabled: pageIndex === 0 || isLoading, onClick: () => goToPage(pageIndex - 1), type: "button", children: "Prev" }), _jsxs("span", { style: styles.pageInfo, children: ["Page ", pageIndex + 1, " of ", totalPages] }), _jsx("button", { style: styles.pageButton, disabled: !hasMore || isLoading, onClick: () => goToPage(pageIndex + 1), type: "button", children: "Next" }), _jsxs("span", { style: styles.rangeInfo, children: ["Showing ", rangeStart, "-", rangeEnd, " of ", totalCount] })] })), _jsxs("div", { style: styles.versionFooter, children: ["@geniee-inc/sfa-kit v", SFA_KIT_VERSION] })] }));
|
|
39
|
+
}
|
|
40
|
+
function autoDetectColumns(items) {
|
|
41
|
+
const first = items[0];
|
|
42
|
+
if (!first)
|
|
43
|
+
return [];
|
|
44
|
+
return Object.keys(first.fields)
|
|
45
|
+
.slice(0, MAX_AUTO_COLUMNS)
|
|
46
|
+
.map(fieldName => ({ fieldName }));
|
|
47
|
+
}
|
|
48
|
+
const styles = {
|
|
49
|
+
container: Object.assign(Object.assign({}, baseContainerStyle), { margin: `${space.lg}px auto`, padding: space.lg, border: `1px solid ${color.border}`, borderRadius: radius.sm, backgroundColor: color.bgSurface }),
|
|
50
|
+
header: {
|
|
51
|
+
display: 'flex',
|
|
52
|
+
justifyContent: 'space-between',
|
|
53
|
+
alignItems: 'center',
|
|
54
|
+
marginBottom: space.sm,
|
|
55
|
+
},
|
|
56
|
+
collectionName: {
|
|
57
|
+
fontSize: font.sizeXl,
|
|
58
|
+
fontWeight: font.weightMedium,
|
|
59
|
+
},
|
|
60
|
+
count: {
|
|
61
|
+
fontSize: font.sizeMd,
|
|
62
|
+
color: color.textSecondary,
|
|
63
|
+
},
|
|
64
|
+
tableWrapper: {
|
|
65
|
+
overflowX: 'auto',
|
|
66
|
+
},
|
|
67
|
+
table: {
|
|
68
|
+
width: '100%',
|
|
69
|
+
borderCollapse: 'collapse',
|
|
70
|
+
fontSize: font.sizeMd,
|
|
71
|
+
},
|
|
72
|
+
th: {
|
|
73
|
+
textAlign: 'left',
|
|
74
|
+
padding: `${space.sm}px ${space.sm}px`,
|
|
75
|
+
borderBottom: `2px solid ${color.border}`,
|
|
76
|
+
fontWeight: font.weightMedium,
|
|
77
|
+
color: color.textSecondary,
|
|
78
|
+
fontSize: font.sizeSm,
|
|
79
|
+
whiteSpace: 'nowrap',
|
|
80
|
+
},
|
|
81
|
+
row: {
|
|
82
|
+
borderBottom: `1px solid ${color.borderLight}`,
|
|
83
|
+
},
|
|
84
|
+
clickableRow: {
|
|
85
|
+
borderBottom: `1px solid ${color.borderLight}`,
|
|
86
|
+
cursor: 'pointer',
|
|
87
|
+
},
|
|
88
|
+
td: {
|
|
89
|
+
padding: `${space.sm}px ${space.sm}px`,
|
|
90
|
+
verticalAlign: 'top',
|
|
91
|
+
maxWidth: 300,
|
|
92
|
+
overflow: 'hidden',
|
|
93
|
+
textOverflow: 'ellipsis',
|
|
94
|
+
},
|
|
95
|
+
loadingCell: {
|
|
96
|
+
padding: space.xl,
|
|
97
|
+
textAlign: 'center',
|
|
98
|
+
color: color.textMuted,
|
|
99
|
+
},
|
|
100
|
+
emptyCell: {
|
|
101
|
+
padding: space.xl,
|
|
102
|
+
textAlign: 'center',
|
|
103
|
+
color: color.textMuted,
|
|
104
|
+
},
|
|
105
|
+
emptyValue: {
|
|
106
|
+
color: color.textMuted,
|
|
107
|
+
},
|
|
108
|
+
pagination: {
|
|
109
|
+
display: 'flex',
|
|
110
|
+
alignItems: 'center',
|
|
111
|
+
gap: space.sm,
|
|
112
|
+
marginTop: space.sm,
|
|
113
|
+
fontSize: font.sizeMd,
|
|
114
|
+
color: color.textSecondary,
|
|
115
|
+
},
|
|
116
|
+
pageButton: Object.assign(Object.assign({}, baseButtonStyle), { padding: `${space.xs}px ${space.sm}px`, border: `1px solid ${color.border}`, backgroundColor: color.bgSurface, color: color.textPrimary }),
|
|
117
|
+
pageInfo: {
|
|
118
|
+
fontWeight: font.weightMedium,
|
|
119
|
+
},
|
|
120
|
+
rangeInfo: {
|
|
121
|
+
marginLeft: 'auto',
|
|
122
|
+
fontSize: font.sizeSm,
|
|
123
|
+
color: color.textMuted,
|
|
124
|
+
},
|
|
125
|
+
error: {
|
|
126
|
+
padding: space.sm,
|
|
127
|
+
backgroundColor: color.errorLight,
|
|
128
|
+
border: `1px solid ${color.error}`,
|
|
129
|
+
borderRadius: radius.sm,
|
|
130
|
+
color: color.error,
|
|
131
|
+
fontSize: font.sizeMd,
|
|
132
|
+
},
|
|
133
|
+
versionFooter: {
|
|
134
|
+
textAlign: 'right',
|
|
135
|
+
fontSize: font.sizeXs,
|
|
136
|
+
color: color.textMuted,
|
|
137
|
+
marginTop: space.sm,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FieldGroupSpec } from '../provider/types';
|
|
2
|
+
interface RecordViewProps {
|
|
3
|
+
readonly collection: string;
|
|
4
|
+
readonly recordId: string;
|
|
5
|
+
readonly title?: string;
|
|
6
|
+
readonly fields?: readonly string[];
|
|
7
|
+
readonly fieldGroups?: readonly FieldGroupSpec[];
|
|
8
|
+
readonly onError?: (error: Error) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* @description Fetches and displays a single SFA/CRM record in a label-value grid.
|
|
12
|
+
*/
|
|
13
|
+
export declare function RecordView({ collection, recordId, title, fields, fieldGroups, onError, }: RecordViewProps): JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { useRecord } from '../hooks/useRecord';
|
|
4
|
+
import { color, font, radius, space, baseContainerStyle } from '../theme';
|
|
5
|
+
import { SFA_KIT_VERSION } from '../version';
|
|
6
|
+
import { FieldRenderer } from './FieldRenderer';
|
|
7
|
+
/**
|
|
8
|
+
* @description Fetches and displays a single SFA/CRM record in a label-value grid.
|
|
9
|
+
*/
|
|
10
|
+
export function RecordView({ collection, recordId, title, fields, fieldGroups, onError, }) {
|
|
11
|
+
var _a;
|
|
12
|
+
const { record, isLoading, error } = useRecord(collection, recordId);
|
|
13
|
+
// Use a ref so inline onError callbacks don't re-fire the effect on
|
|
14
|
+
// every parent render when error is present.
|
|
15
|
+
const onErrorRef = useRef(onError);
|
|
16
|
+
onErrorRef.current = onError;
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
var _a;
|
|
19
|
+
if (error)
|
|
20
|
+
(_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, error);
|
|
21
|
+
}, [error]);
|
|
22
|
+
if (error) {
|
|
23
|
+
return (_jsxs("div", { style: styles.container, children: [_jsx("div", { style: styles.error, children: error.message }), _jsxs("div", { style: styles.versionFooter, children: ["@geniee-inc/sfa-kit v", SFA_KIT_VERSION] })] }));
|
|
24
|
+
}
|
|
25
|
+
if (isLoading || !record) {
|
|
26
|
+
return (_jsxs("div", { style: styles.container, children: [_jsx("div", { style: styles.loading, children: "Loading..." }), _jsxs("div", { style: styles.versionFooter, children: ["@geniee-inc/sfa-kit v", SFA_KIT_VERSION] })] }));
|
|
27
|
+
}
|
|
28
|
+
const displayTitle = (_a = title !== null && title !== void 0 ? title : record.title) !== null && _a !== void 0 ? _a : buildTitle(record.fields, record.titleFields);
|
|
29
|
+
const hasGroups = fieldGroups && fieldGroups.length > 0;
|
|
30
|
+
return (_jsxs("div", { style: styles.container, children: [displayTitle && _jsx("h2", { style: styles.title, children: displayTitle }), _jsxs("div", { style: styles.meta, children: [collection, " / ", record.record_id] }), hasGroups
|
|
31
|
+
? renderFieldGroups(record.fields, fieldGroups)
|
|
32
|
+
: renderFlatFields(record.fields, fields), _jsxs("div", { style: styles.versionFooter, children: ["@geniee-inc/sfa-kit v", SFA_KIT_VERSION] })] }));
|
|
33
|
+
}
|
|
34
|
+
const LAYOUT_COLUMNS = {
|
|
35
|
+
'1-col': 1,
|
|
36
|
+
'2-col': 2,
|
|
37
|
+
'3-col': 3,
|
|
38
|
+
};
|
|
39
|
+
function renderFlatFields(mapping, fields) {
|
|
40
|
+
const entries = getFieldEntries(mapping, fields);
|
|
41
|
+
if (entries.length === 0) {
|
|
42
|
+
return _jsx("div", { style: styles.empty, children: "No fields to display" });
|
|
43
|
+
}
|
|
44
|
+
return (_jsx("div", { style: styles.grid, children: entries.map(([name, value]) => (_jsxs("div", { style: styles.row, children: [_jsx("div", { style: styles.label, children: name }), _jsx("div", { style: styles.value, children: _jsx(FieldRenderer, { fieldValue: value, fieldName: name }) })] }, name))) }));
|
|
45
|
+
}
|
|
46
|
+
function renderFieldGroups(mapping, groups) {
|
|
47
|
+
return (_jsx("div", { style: styles.groupsContainer, children: groups.map((group, groupIndex) => {
|
|
48
|
+
var _a;
|
|
49
|
+
const entries = getFieldEntries(mapping, group.fields);
|
|
50
|
+
if (entries.length === 0)
|
|
51
|
+
return null;
|
|
52
|
+
const columnCount = LAYOUT_COLUMNS[(_a = group.layout) !== null && _a !== void 0 ? _a : '1-col'];
|
|
53
|
+
const gridStyle = {
|
|
54
|
+
display: 'grid',
|
|
55
|
+
gridTemplateColumns: `repeat(${columnCount}, 1fr)`,
|
|
56
|
+
gap: '0 24px',
|
|
57
|
+
};
|
|
58
|
+
return (_jsxs("div", { style: styles.group, children: [group.title && _jsx("h4", { style: styles.groupTitle, children: group.title }), _jsx("div", { style: gridStyle, children: entries.map(([name, value]) => (_jsxs("div", { style: styles.row, children: [_jsx("div", { style: styles.label, children: name }), _jsx("div", { style: styles.value, children: _jsx(FieldRenderer, { fieldValue: value, fieldName: name }) })] }, name))) })] }, groupIndex));
|
|
59
|
+
}) }));
|
|
60
|
+
}
|
|
61
|
+
function buildTitle(mapping, titleFields) {
|
|
62
|
+
return titleFields
|
|
63
|
+
.map(name => { var _a; return (_a = mapping[name]) === null || _a === void 0 ? void 0 : _a.label; })
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.join(' ');
|
|
66
|
+
}
|
|
67
|
+
function getFieldEntries(mapping, fields) {
|
|
68
|
+
if (fields) {
|
|
69
|
+
return fields
|
|
70
|
+
.filter(name => mapping[name] != null)
|
|
71
|
+
.map(name => [name, mapping[name]]);
|
|
72
|
+
}
|
|
73
|
+
return Object.entries(mapping);
|
|
74
|
+
}
|
|
75
|
+
const styles = {
|
|
76
|
+
container: Object.assign(Object.assign({}, baseContainerStyle), { maxWidth: 720, margin: `${space.lg}px auto`, padding: space.lg, border: `1px solid ${color.border}`, borderRadius: radius.sm, backgroundColor: color.bgSurface }),
|
|
77
|
+
title: {
|
|
78
|
+
fontSize: font.sizeTitle,
|
|
79
|
+
fontWeight: font.weightMedium,
|
|
80
|
+
margin: '0 0 4px',
|
|
81
|
+
},
|
|
82
|
+
meta: {
|
|
83
|
+
fontSize: font.sizeSm,
|
|
84
|
+
color: color.textMuted,
|
|
85
|
+
fontFamily: 'monospace',
|
|
86
|
+
marginBottom: space.md,
|
|
87
|
+
},
|
|
88
|
+
grid: {
|
|
89
|
+
display: 'flex',
|
|
90
|
+
flexDirection: 'column',
|
|
91
|
+
gap: 0,
|
|
92
|
+
},
|
|
93
|
+
row: {
|
|
94
|
+
display: 'flex',
|
|
95
|
+
padding: `${space.sm}px 0`,
|
|
96
|
+
borderBottom: `1px solid ${color.borderLight}`,
|
|
97
|
+
gap: space.md,
|
|
98
|
+
fontSize: font.sizeLg,
|
|
99
|
+
},
|
|
100
|
+
label: {
|
|
101
|
+
fontWeight: font.weightRegular,
|
|
102
|
+
color: color.textSecondary,
|
|
103
|
+
minWidth: 180,
|
|
104
|
+
flexShrink: 0,
|
|
105
|
+
fontSize: font.sizeSm,
|
|
106
|
+
},
|
|
107
|
+
value: {
|
|
108
|
+
color: color.textPrimary,
|
|
109
|
+
wordBreak: 'break-all',
|
|
110
|
+
flex: 1,
|
|
111
|
+
},
|
|
112
|
+
groupsContainer: {
|
|
113
|
+
display: 'flex',
|
|
114
|
+
flexDirection: 'column',
|
|
115
|
+
gap: 20,
|
|
116
|
+
},
|
|
117
|
+
group: {
|
|
118
|
+
borderTop: `1px solid ${color.border}`,
|
|
119
|
+
paddingTop: space.sm,
|
|
120
|
+
},
|
|
121
|
+
groupTitle: {
|
|
122
|
+
fontSize: font.sizeMd,
|
|
123
|
+
fontWeight: font.weightMedium,
|
|
124
|
+
color: color.textPrimary,
|
|
125
|
+
margin: `0 0 ${space.sm}px`,
|
|
126
|
+
},
|
|
127
|
+
loading: {
|
|
128
|
+
padding: space.lg,
|
|
129
|
+
textAlign: 'center',
|
|
130
|
+
color: color.textMuted,
|
|
131
|
+
fontSize: font.sizeLg,
|
|
132
|
+
},
|
|
133
|
+
error: {
|
|
134
|
+
padding: space.sm,
|
|
135
|
+
backgroundColor: color.errorLight,
|
|
136
|
+
border: `1px solid ${color.error}`,
|
|
137
|
+
borderRadius: radius.sm,
|
|
138
|
+
color: color.error,
|
|
139
|
+
fontSize: font.sizeMd,
|
|
140
|
+
},
|
|
141
|
+
empty: {
|
|
142
|
+
padding: space.lg,
|
|
143
|
+
textAlign: 'center',
|
|
144
|
+
color: color.textMuted,
|
|
145
|
+
fontSize: font.sizeLg,
|
|
146
|
+
},
|
|
147
|
+
versionFooter: {
|
|
148
|
+
textAlign: 'right',
|
|
149
|
+
fontSize: font.sizeXs,
|
|
150
|
+
color: color.textMuted,
|
|
151
|
+
marginTop: space.sm,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description sfa-kit のユーザー向けエラーメッセージ定数。
|
|
3
|
+
* AI 生成ページ内で表示されるため、日本語で記載する。
|
|
4
|
+
*/
|
|
5
|
+
/** SfaProvider コンテキスト外で hook が使用された場合 */
|
|
6
|
+
export declare const ERR_NO_PROVIDER = "SfaProvider \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u3053\u306E\u30D5\u30C3\u30AF\u306F SfaProvider \u306E\u5185\u90E8\u3067\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
|
|
7
|
+
/**
|
|
8
|
+
* プレビューモード(JAI Studio)で書き込み操作が試行された場合。
|
|
9
|
+
* 本番の SFA/CRM に埋め込まれたページでのみ書き込みが許可される。
|
|
10
|
+
*/
|
|
11
|
+
export declare const ERR_READ_ONLY = "\u66F8\u304D\u8FBC\u307F\u64CD\u4F5C\u306F\u30D7\u30EC\u30D3\u30E5\u30FC\u30E2\u30FC\u30C9\u3067\u306F\u7121\u52B9\u3067\u3059\u3002\u30C7\u30FC\u30BF\u3092\u5909\u66F4\u3059\u308B\u306B\u306F\u3001\u3053\u306E\u30DA\u30FC\u30B8\u3092 GENIEE SFA/CRM \u306B\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3057\u3066\u3054\u5229\u7528\u304F\u3060\u3055\u3044\u3002";
|
|
12
|
+
/** proxy モードで mutation メソッドが呼ばれた場合(開発者向け) */
|
|
13
|
+
export declare const ERR_DIRECT_MODE_REQUIRED = "\u3053\u306E\u64CD\u4F5C\u306F direct \u30E2\u30FC\u30C9\uFF08Keystone v1 API\uFF09\u3067\u306E\u307F\u5229\u7528\u53EF\u80FD\u3067\u3059\u3002";
|
|
14
|
+
/** record_id が空の状態で API 呼び出しが試行された場合 */
|
|
15
|
+
export declare const ERR_EMPTY_RECORD_ID = "\u30EC\u30B3\u30FC\u30C9 ID \u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u8868\u793A\u3059\u308B\u30EC\u30B3\u30FC\u30C9\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description sfa-kit のユーザー向けエラーメッセージ定数。
|
|
3
|
+
* AI 生成ページ内で表示されるため、日本語で記載する。
|
|
4
|
+
*/
|
|
5
|
+
/** SfaProvider コンテキスト外で hook が使用された場合 */
|
|
6
|
+
export const ERR_NO_PROVIDER = 'SfaProvider が見つかりません。このフックは SfaProvider の内部で使用してください。';
|
|
7
|
+
/**
|
|
8
|
+
* プレビューモード(JAI Studio)で書き込み操作が試行された場合。
|
|
9
|
+
* 本番の SFA/CRM に埋め込まれたページでのみ書き込みが許可される。
|
|
10
|
+
*/
|
|
11
|
+
export const ERR_READ_ONLY = '書き込み操作はプレビューモードでは無効です。データを変更するには、このページを GENIEE SFA/CRM にアップロードしてご利用ください。';
|
|
12
|
+
/** proxy モードで mutation メソッドが呼ばれた場合(開発者向け) */
|
|
13
|
+
export const ERR_DIRECT_MODE_REQUIRED = 'この操作は direct モード(Keystone v1 API)でのみ利用可能です。';
|
|
14
|
+
/** record_id が空の状態で API 呼び出しが試行された場合 */
|
|
15
|
+
export const ERR_EMPTY_RECORD_ID = 'レコード ID が指定されていません。表示するレコードを選択してください。';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SfaCreateOptions, SfaRecord } from '../provider/types';
|
|
2
|
+
export interface UseCreateRecordResult {
|
|
3
|
+
/** Call to create a new record. Returns the created record with its version. */
|
|
4
|
+
readonly create: (fields: Record<string, unknown>, options?: SfaCreateOptions) => Promise<SfaRecord>;
|
|
5
|
+
/** The most recently created record, or null before the first create. */
|
|
6
|
+
readonly data: SfaRecord | null;
|
|
7
|
+
readonly isLoading: boolean;
|
|
8
|
+
readonly error: Error | null;
|
|
9
|
+
/** Reset data and error state to initial values. */
|
|
10
|
+
readonly reset: () => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* @description Imperative hook for creating a single record. Unlike query
|
|
14
|
+
* hooks, this does not auto-fetch on mount. Call `create(fields)` to trigger
|
|
15
|
+
* the POST and track loading/error/data state.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useCreateRecord(collection: string): UseCreateRecordResult;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { useCallback, useRef, useState } from 'react';
|
|
3
|
+
import { ERR_READ_ONLY } from '../errors';
|
|
4
|
+
import { useSfaContext } from '../provider/useSfaClient';
|
|
5
|
+
/**
|
|
6
|
+
* @description Imperative hook for creating a single record. Unlike query
|
|
7
|
+
* hooks, this does not auto-fetch on mount. Call `create(fields)` to trigger
|
|
8
|
+
* the POST and track loading/error/data state.
|
|
9
|
+
*/
|
|
10
|
+
export function useCreateRecord(collection) {
|
|
11
|
+
const { client, readOnly } = useSfaContext();
|
|
12
|
+
const [data, setData] = useState(null);
|
|
13
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
14
|
+
const [error, setError] = useState(null);
|
|
15
|
+
const callId = useRef(0);
|
|
16
|
+
const create = useCallback((fields, options) => __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
callId.current += 1;
|
|
18
|
+
const currentCallId = callId.current;
|
|
19
|
+
if (readOnly) {
|
|
20
|
+
throw new Error(ERR_READ_ONLY);
|
|
21
|
+
}
|
|
22
|
+
setIsLoading(true);
|
|
23
|
+
setError(null);
|
|
24
|
+
try {
|
|
25
|
+
const record = yield client.create(collection, fields, options);
|
|
26
|
+
if (currentCallId === callId.current) {
|
|
27
|
+
setData(record);
|
|
28
|
+
setIsLoading(false);
|
|
29
|
+
}
|
|
30
|
+
return record;
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
if (currentCallId === callId.current) {
|
|
34
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
35
|
+
setError(err);
|
|
36
|
+
setIsLoading(false);
|
|
37
|
+
}
|
|
38
|
+
throw e;
|
|
39
|
+
}
|
|
40
|
+
}), [client, collection, readOnly]);
|
|
41
|
+
const reset = useCallback(() => {
|
|
42
|
+
callId.current += 1;
|
|
43
|
+
setData(null);
|
|
44
|
+
setError(null);
|
|
45
|
+
setIsLoading(false);
|
|
46
|
+
}, []);
|
|
47
|
+
return { create, data, isLoading, error, reset };
|
|
48
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface UseDeleteRecordResult {
|
|
2
|
+
/** Call to delete a record. Requires the record's current version for optimistic locking. */
|
|
3
|
+
readonly remove: (recordId: string, version: string) => Promise<void>;
|
|
4
|
+
readonly isLoading: boolean;
|
|
5
|
+
readonly error: Error | null;
|
|
6
|
+
/** True after a successful delete, until reset() is called. */
|
|
7
|
+
readonly isSuccess: boolean;
|
|
8
|
+
/** Reset error and success state to initial values. */
|
|
9
|
+
readonly reset: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @description Imperative hook for deleting a single record with optimistic
|
|
13
|
+
* locking. Tracks loading, error, and success state. DELETE returns no body
|
|
14
|
+
* (204), so this hook exposes `isSuccess` instead of `data`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useDeleteRecord(collection: string): UseDeleteRecordResult;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { useCallback, useRef, useState } from 'react';
|
|
3
|
+
import { ERR_READ_ONLY } from '../errors';
|
|
4
|
+
import { useSfaContext } from '../provider/useSfaClient';
|
|
5
|
+
/**
|
|
6
|
+
* @description Imperative hook for deleting a single record with optimistic
|
|
7
|
+
* locking. Tracks loading, error, and success state. DELETE returns no body
|
|
8
|
+
* (204), so this hook exposes `isSuccess` instead of `data`.
|
|
9
|
+
*/
|
|
10
|
+
export function useDeleteRecord(collection) {
|
|
11
|
+
const { client, readOnly } = useSfaContext();
|
|
12
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
13
|
+
const [error, setError] = useState(null);
|
|
14
|
+
const [isSuccess, setIsSuccess] = useState(false);
|
|
15
|
+
const callId = useRef(0);
|
|
16
|
+
const remove = useCallback((recordId, version) => __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
callId.current += 1;
|
|
18
|
+
const currentCallId = callId.current;
|
|
19
|
+
if (readOnly) {
|
|
20
|
+
throw new Error(ERR_READ_ONLY);
|
|
21
|
+
}
|
|
22
|
+
setIsLoading(true);
|
|
23
|
+
setError(null);
|
|
24
|
+
setIsSuccess(false);
|
|
25
|
+
try {
|
|
26
|
+
yield client.delete(collection, recordId, version);
|
|
27
|
+
if (currentCallId === callId.current) {
|
|
28
|
+
setIsSuccess(true);
|
|
29
|
+
setIsLoading(false);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
if (currentCallId === callId.current) {
|
|
34
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
35
|
+
setError(err);
|
|
36
|
+
setIsLoading(false);
|
|
37
|
+
}
|
|
38
|
+
throw e;
|
|
39
|
+
}
|
|
40
|
+
}), [client, collection, readOnly]);
|
|
41
|
+
const reset = useCallback(() => {
|
|
42
|
+
callId.current += 1;
|
|
43
|
+
setError(null);
|
|
44
|
+
setIsSuccess(false);
|
|
45
|
+
setIsLoading(false);
|
|
46
|
+
}, []);
|
|
47
|
+
return { remove, isLoading, error, isSuccess, reset };
|
|
48
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SfaObjectDetail } from '../provider/types';
|
|
2
|
+
interface UseDescribeOptions {
|
|
3
|
+
readonly enabled?: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface UseDescribeResult {
|
|
6
|
+
readonly object: SfaObjectDetail | null;
|
|
7
|
+
readonly isLoading: boolean;
|
|
8
|
+
readonly error: Error | null;
|
|
9
|
+
readonly refetch: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @description Fetches metadata for an object: fields, types, relationships, permissions.
|
|
13
|
+
* The AI agent calls this to discover schema before rendering.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useDescribe(objectKey: string, options?: UseDescribeOptions): UseDescribeResult;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useSfaClient } from '../provider/useSfaClient';
|
|
3
|
+
/**
|
|
4
|
+
* @description Fetches metadata for an object: fields, types, relationships, permissions.
|
|
5
|
+
* The AI agent calls this to discover schema before rendering.
|
|
6
|
+
*/
|
|
7
|
+
export function useDescribe(objectKey, options) {
|
|
8
|
+
var _a;
|
|
9
|
+
const client = useSfaClient();
|
|
10
|
+
const enabled = (_a = options === null || options === void 0 ? void 0 : options.enabled) !== null && _a !== void 0 ? _a : true;
|
|
11
|
+
const [object, setObject] = useState(null);
|
|
12
|
+
const [isLoading, setIsLoading] = useState(enabled && !!objectKey);
|
|
13
|
+
const [error, setError] = useState(null);
|
|
14
|
+
const fetchIdRef = useRef(0);
|
|
15
|
+
const doFetch = useCallback(() => {
|
|
16
|
+
if (!objectKey || !enabled)
|
|
17
|
+
return;
|
|
18
|
+
const fetchId = ++fetchIdRef.current;
|
|
19
|
+
setIsLoading(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
client
|
|
22
|
+
.describe(objectKey)
|
|
23
|
+
.then(result => {
|
|
24
|
+
if (fetchId !== fetchIdRef.current)
|
|
25
|
+
return;
|
|
26
|
+
setObject(result.object);
|
|
27
|
+
setIsLoading(false);
|
|
28
|
+
})
|
|
29
|
+
.catch((err) => {
|
|
30
|
+
if (fetchId !== fetchIdRef.current)
|
|
31
|
+
return;
|
|
32
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
33
|
+
setIsLoading(false);
|
|
34
|
+
});
|
|
35
|
+
}, [client, objectKey, enabled]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
doFetch();
|
|
38
|
+
}, [doFetch]);
|
|
39
|
+
return { object, isLoading, error, refetch: doFetch };
|
|
40
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SfaFieldTypeDescriptor } from '../provider/types';
|
|
2
|
+
interface UseFieldTypesOptions {
|
|
3
|
+
readonly enabled?: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface UseFieldTypesResult {
|
|
6
|
+
readonly fieldTypes: readonly SfaFieldTypeDescriptor[];
|
|
7
|
+
readonly isLoading: boolean;
|
|
8
|
+
readonly error: Error | null;
|
|
9
|
+
readonly refetch: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @description Fetches the static field-type catalog from Keystone.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useFieldTypes(options?: UseFieldTypesOptions): UseFieldTypesResult;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useSfaClient } from '../provider/useSfaClient';
|
|
3
|
+
/**
|
|
4
|
+
* @description Fetches the static field-type catalog from Keystone.
|
|
5
|
+
*/
|
|
6
|
+
export function useFieldTypes(options) {
|
|
7
|
+
var _a;
|
|
8
|
+
const client = useSfaClient();
|
|
9
|
+
const enabled = (_a = options === null || options === void 0 ? void 0 : options.enabled) !== null && _a !== void 0 ? _a : true;
|
|
10
|
+
const [fieldTypes, setFieldTypes] = useState([]);
|
|
11
|
+
const [isLoading, setIsLoading] = useState(enabled);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const fetchIdRef = useRef(0);
|
|
14
|
+
const doFetch = useCallback(() => {
|
|
15
|
+
if (!enabled)
|
|
16
|
+
return;
|
|
17
|
+
const fetchId = ++fetchIdRef.current;
|
|
18
|
+
setIsLoading(true);
|
|
19
|
+
setError(null);
|
|
20
|
+
client
|
|
21
|
+
.listFieldTypes()
|
|
22
|
+
.then(result => {
|
|
23
|
+
if (fetchId !== fetchIdRef.current)
|
|
24
|
+
return;
|
|
25
|
+
setFieldTypes(result.fieldTypes);
|
|
26
|
+
setIsLoading(false);
|
|
27
|
+
})
|
|
28
|
+
.catch((err) => {
|
|
29
|
+
if (fetchId !== fetchIdRef.current)
|
|
30
|
+
return;
|
|
31
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
32
|
+
setIsLoading(false);
|
|
33
|
+
});
|
|
34
|
+
}, [client, enabled]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
doFetch();
|
|
37
|
+
}, [doFetch]);
|
|
38
|
+
return { fieldTypes, isLoading, error, refetch: doFetch };
|
|
39
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SfaObjectSummary } from '../provider/types';
|
|
2
|
+
interface UseObjectCatalogOptions {
|
|
3
|
+
readonly enabled?: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface UseObjectCatalogResult {
|
|
6
|
+
readonly objects: readonly SfaObjectSummary[];
|
|
7
|
+
readonly isLoading: boolean;
|
|
8
|
+
readonly error: Error | null;
|
|
9
|
+
readonly refetch: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @description Fetches the list of all objects available in the org.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useObjectCatalog(options?: UseObjectCatalogOptions): UseObjectCatalogResult;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useSfaClient } from '../provider/useSfaClient';
|
|
3
|
+
/**
|
|
4
|
+
* @description Fetches the list of all objects available in the org.
|
|
5
|
+
*/
|
|
6
|
+
export function useObjectCatalog(options) {
|
|
7
|
+
var _a;
|
|
8
|
+
const client = useSfaClient();
|
|
9
|
+
const enabled = (_a = options === null || options === void 0 ? void 0 : options.enabled) !== null && _a !== void 0 ? _a : true;
|
|
10
|
+
const [objects, setObjects] = useState([]);
|
|
11
|
+
const [isLoading, setIsLoading] = useState(enabled);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
const fetchIdRef = useRef(0);
|
|
14
|
+
const doFetch = useCallback(() => {
|
|
15
|
+
if (!enabled)
|
|
16
|
+
return;
|
|
17
|
+
const fetchId = ++fetchIdRef.current;
|
|
18
|
+
setIsLoading(true);
|
|
19
|
+
setError(null);
|
|
20
|
+
client
|
|
21
|
+
.listObjects()
|
|
22
|
+
.then(result => {
|
|
23
|
+
if (fetchId !== fetchIdRef.current)
|
|
24
|
+
return;
|
|
25
|
+
setObjects(result.objects);
|
|
26
|
+
setIsLoading(false);
|
|
27
|
+
})
|
|
28
|
+
.catch((err) => {
|
|
29
|
+
if (fetchId !== fetchIdRef.current)
|
|
30
|
+
return;
|
|
31
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
32
|
+
setIsLoading(false);
|
|
33
|
+
});
|
|
34
|
+
}, [client, enabled]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
doFetch();
|
|
37
|
+
}, [doFetch]);
|
|
38
|
+
return { objects, isLoading, error, refetch: doFetch };
|
|
39
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SfaRecord } from '../provider/types';
|
|
2
|
+
interface UseRecordOptions {
|
|
3
|
+
readonly enabled?: boolean;
|
|
4
|
+
}
|
|
5
|
+
interface UseRecordResult {
|
|
6
|
+
readonly record: SfaRecord | null;
|
|
7
|
+
readonly isLoading: boolean;
|
|
8
|
+
readonly error: Error | null;
|
|
9
|
+
readonly refetch: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @description Fetches a single record from the SFA/CRM API.
|
|
13
|
+
*/
|
|
14
|
+
export declare function useRecord(collection: string, recordId: string, options?: UseRecordOptions): UseRecordResult;
|
|
15
|
+
export {};
|