Skip to content

Commit 8f62a7e

Browse files
authored
feat(compass-collection): add Schema Editor UI - Mock Data Generator CLOUDP-333854 (#7295)
1 parent 0626ec8 commit 8f62a7e

File tree

10 files changed

+688
-17
lines changed

10 files changed

+688
-17
lines changed

package-lock.json

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-collection/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"reformat": "npm run eslint . -- --fix && npm run prettier -- --write ."
4949
},
5050
"dependencies": {
51+
"@faker-js/faker": "^9.0.0",
5152
"@mongodb-js/compass-app-registry": "^9.4.22",
5253
"@mongodb-js/compass-app-stores": "^7.59.0",
5354
"@mongodb-js/compass-components": "^1.51.0",
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {
2+
Banner,
3+
BannerVariant,
4+
Body,
5+
css,
6+
Option,
7+
palette,
8+
Select,
9+
spacing,
10+
} from '@mongodb-js/compass-components';
11+
import React from 'react';
12+
import { UNRECOGNIZED_FAKER_METHOD } from '../../modules/collection-tab';
13+
14+
const fieldMappingSelectorsStyles = css({
15+
width: '50%',
16+
display: 'flex',
17+
flexDirection: 'column',
18+
gap: spacing[200],
19+
});
20+
21+
const labelStyles = css({
22+
color: palette.gray.dark1,
23+
fontWeight: 600,
24+
});
25+
26+
interface Props {
27+
activeJsonType: string;
28+
activeFakerFunction: string;
29+
onJsonTypeSelect: (jsonType: string) => void;
30+
onFakerFunctionSelect: (fakerFunction: string) => void;
31+
}
32+
33+
const FakerMappingSelector = ({
34+
activeJsonType,
35+
activeFakerFunction,
36+
onJsonTypeSelect,
37+
onFakerFunctionSelect,
38+
}: Props) => {
39+
return (
40+
<div className={fieldMappingSelectorsStyles}>
41+
<Body className={labelStyles}>Mapping</Body>
42+
<Select
43+
label="JSON Type"
44+
allowDeselect={false}
45+
value={activeJsonType}
46+
onChange={onJsonTypeSelect}
47+
>
48+
{/* TODO(CLOUDP-344400) : Make the select input editable and render other options depending on the JSON type selected */}
49+
{[activeJsonType].map((type) => (
50+
<Option key={type} value={type}>
51+
{type}
52+
</Option>
53+
))}
54+
</Select>
55+
<Select
56+
label="Faker Function"
57+
allowDeselect={false}
58+
value={activeFakerFunction}
59+
onChange={onFakerFunctionSelect}
60+
>
61+
{/* TODO(CLOUDP-344400): Make the select input editable and render other JSON types */}
62+
{[activeFakerFunction].map((field) => (
63+
<Option key={field} value={field}>
64+
{field}
65+
</Option>
66+
))}
67+
</Select>
68+
{activeFakerFunction === UNRECOGNIZED_FAKER_METHOD && (
69+
<Banner variant={BannerVariant.Warning}>
70+
Please select a function or we will default fill this field with the
71+
string &quot;Unrecognized&quot;
72+
</Banner>
73+
)}
74+
{/* TODO(CLOUDP-344400): Render faker function parameters once we have a way to validate them. */}
75+
</div>
76+
);
77+
};
78+
79+
export default FakerMappingSelector;

packages/compass-collection/src/components/mock-data-generator-modal/faker-schema-editor-screen.tsx

Lines changed: 166 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,172 @@
1+
import {
2+
Body,
3+
Button,
4+
ButtonSize,
5+
ButtonVariant,
6+
css,
7+
Link,
8+
palette,
9+
spacing,
10+
SpinLoaderWithLabel,
11+
} from '@mongodb-js/compass-components';
112
import React from 'react';
13+
import FieldSelector from './schema-field-selector';
14+
import FakerMappingSelector from './faker-mapping-selector';
15+
import type { FakerSchemaMapping, MockDataGeneratorState } from './types';
216

3-
// TODO: More to come from CLOUDP-333853, CLOUDP-333854
4-
const FakerSchemaEditorScreen = () => {
17+
const containerStyles = css({
18+
display: 'flex',
19+
flexDirection: 'column',
20+
gap: spacing[400],
21+
});
22+
23+
const innerEditorStyles = css({
24+
display: 'flex',
25+
flexDirection: 'row',
26+
justifyContent: 'space-between',
27+
});
28+
29+
const titleStyles = css({
30+
color: palette.black,
31+
fontWeight: 600,
32+
fontSize: '16px',
33+
lineHeight: '20px',
34+
marginBottom: 0,
35+
});
36+
37+
const bodyStyles = css({
38+
color: palette.gray.dark1,
39+
});
40+
41+
const confirmMappingsButtonStyles = css({
42+
width: '200px',
43+
});
44+
45+
const schemaEditorLoaderStyles = css({
46+
display: 'flex',
47+
alignItems: 'center',
48+
justifyContent: 'center',
49+
});
50+
51+
const FakerSchemaEditorContent = ({
52+
fakerSchemaMappings,
53+
onSchemaConfirmed,
54+
}: {
55+
fakerSchemaMappings: FakerSchemaMapping[];
56+
onSchemaConfirmed: (isConfirmed: boolean) => void;
57+
}) => {
58+
const [fakerSchemaFormValues, setFakerSchemaFormValues] =
59+
React.useState<Array<FakerSchemaMapping>>(fakerSchemaMappings);
60+
const [activeField, setActiveField] = React.useState<string>(
61+
fakerSchemaFormValues[0].fieldPath
62+
);
63+
64+
const activeJsonType = fakerSchemaFormValues.find(
65+
(mapping) => mapping.fieldPath === activeField
66+
)?.mongoType;
67+
const activeFakerFunction = fakerSchemaFormValues.find(
68+
(mapping) => mapping.fieldPath === activeField
69+
)?.fakerMethod;
70+
71+
const resetIsSchemaConfirmed = () => {
72+
onSchemaConfirmed(false);
73+
};
74+
75+
const onJsonTypeSelect = (newJsonType: string) => {
76+
const updatedFakerFieldMapping = fakerSchemaFormValues.find(
77+
(mapping) => mapping.fieldPath === activeField
78+
);
79+
if (updatedFakerFieldMapping) {
80+
updatedFakerFieldMapping.mongoType = newJsonType;
81+
setFakerSchemaFormValues(
82+
fakerSchemaFormValues.map((mapping) =>
83+
mapping.fieldPath === activeField ? updatedFakerFieldMapping : mapping
84+
)
85+
);
86+
resetIsSchemaConfirmed();
87+
}
88+
};
89+
90+
const onFakerFunctionSelect = (newFakerFunction: string) => {
91+
const updatedFakerFieldMapping = fakerSchemaFormValues.find(
92+
(mapping) => mapping.fieldPath === activeField
93+
);
94+
if (updatedFakerFieldMapping) {
95+
updatedFakerFieldMapping.fakerMethod = newFakerFunction;
96+
setFakerSchemaFormValues(
97+
fakerSchemaFormValues.map((mapping) =>
98+
mapping.fieldPath === activeField ? updatedFakerFieldMapping : mapping
99+
)
100+
);
101+
resetIsSchemaConfirmed();
102+
}
103+
};
104+
105+
return (
106+
<>
107+
<div className={innerEditorStyles}>
108+
<FieldSelector
109+
activeField={activeField}
110+
fields={fakerSchemaFormValues.map((mapping) => mapping.fieldPath)}
111+
onFieldSelect={setActiveField}
112+
/>
113+
{activeJsonType && activeFakerFunction && (
114+
<FakerMappingSelector
115+
activeJsonType={activeJsonType}
116+
activeFakerFunction={activeFakerFunction}
117+
onJsonTypeSelect={onJsonTypeSelect}
118+
onFakerFunctionSelect={onFakerFunctionSelect}
119+
/>
120+
)}
121+
</div>
122+
<Button
123+
size={ButtonSize.Small}
124+
className={confirmMappingsButtonStyles}
125+
variant={ButtonVariant.Primary}
126+
onClick={() => onSchemaConfirmed(true)}
127+
>
128+
Confirm mappings
129+
</Button>
130+
</>
131+
);
132+
};
133+
134+
const FakerSchemaEditorScreen = ({
135+
onSchemaConfirmed,
136+
fakerSchemaGenerationState,
137+
}: {
138+
isSchemaConfirmed: boolean;
139+
onSchemaConfirmed: (isConfirmed: boolean) => void;
140+
fakerSchemaGenerationState: MockDataGeneratorState;
141+
}) => {
5142
return (
6-
<div data-testid="faker-schema-editor">
7-
Schema Editor Content Placeholder
143+
<div data-testid="faker-schema-editor" className={containerStyles}>
144+
<div>
145+
<h3 className={titleStyles}>
146+
Confirm Field to Faker Function Mappings
147+
</h3>
148+
<Body className={bodyStyles}>
149+
We have sampled your collection and created a schema based on your
150+
documents. That schema has been sent to an LLM and it has returned the
151+
following mapping between your schema fields and{' '}
152+
<Link href="https://fakerjs.dev/api/faker.html">faker functions</Link>
153+
.
154+
</Body>
155+
</div>
156+
{fakerSchemaGenerationState.status === 'in-progress' && (
157+
<div
158+
data-testid="faker-schema-editor-loader"
159+
className={schemaEditorLoaderStyles}
160+
>
161+
<SpinLoaderWithLabel progressText="Processing Documents..." />
162+
</div>
163+
)}
164+
{fakerSchemaGenerationState.status === 'completed' && (
165+
<FakerSchemaEditorContent
166+
fakerSchemaMappings={fakerSchemaGenerationState.fakerSchema}
167+
onSchemaConfirmed={onSchemaConfirmed}
168+
/>
169+
)}
8170
</div>
9171
);
10172
};

0 commit comments

Comments
 (0)