betterstart-cli 0.0.108 → 0.0.109
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/assets/adapters/next/templates/init/admin-globals.css +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/media/edit-media-dialog-content.tsx +1 -0
- package/dist/assets/adapters/next/templates/init/components/shared/media/media-url-importer.tsx +89 -78
- package/dist/assets/adapters/next/templates/init/components/shared/search-input.tsx +5 -1
- package/dist/assets/adapters/next/templates/init/pages/forgot-password-form.tsx +41 -36
- package/dist/assets/adapters/next/templates/init/pages/login-form.tsx +94 -70
- package/dist/assets/adapters/next/templates/init/pages/profile/profile-form.tsx +7 -7
- package/dist/assets/adapters/next/templates/init/pages/reset-password-form.tsx +86 -67
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/form-notifications-drawer.tsx +1 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhook-endpoint-dialog.tsx +26 -8
- package/dist/assets/adapters/next/templates/init/pages/users/change-password-dialog.tsx +8 -10
- package/dist/assets/adapters/next/templates/init/pages/users/create-user-dialog.tsx +8 -5
- package/dist/assets/adapters/next/templates/init/pages/users/delete-user-dialog.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/users/edit-role-dialog.tsx +76 -33
- package/dist/assets/shared-assets/react-admin/ui/input-group.tsx +6 -0
- package/dist/cli.js +208 -166
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -16117,9 +16117,11 @@ ${contentStateDestructure}
|
|
|
16117
16117
|
} = state
|
|
16118
16118
|
${contentSidebarVisibilityState}
|
|
16119
16119
|
return (
|
|
16120
|
-
<
|
|
16121
|
-
${
|
|
16122
|
-
|
|
16120
|
+
<Form {...form}>
|
|
16121
|
+
<form autoComplete="off" id="${schemaName}-form" onSubmit={form.handleSubmit((values) => onSubmit(values${hasDraftInitialData}), handleErrors)} ${topLevelTabsFieldClassName}>
|
|
16122
|
+
${formBodyJsx}
|
|
16123
|
+
</form>
|
|
16124
|
+
</Form>
|
|
16123
16125
|
)
|
|
16124
16126
|
}
|
|
16125
16127
|
`;
|
|
@@ -16345,7 +16347,7 @@ ${defaultValues}
|
|
|
16345
16347
|
|
|
16346
16348
|
const form = useForm<FormValues>({
|
|
16347
16349
|
resolver: standardSchemaResolver(formSchema),
|
|
16348
|
-
mode: '
|
|
16350
|
+
mode: 'onChange',
|
|
16349
16351
|
reValidateMode: 'onChange',
|
|
16350
16352
|
defaultValues
|
|
16351
16353
|
})
|
|
@@ -16522,16 +16524,12 @@ function renderFormTabContentDetailsTemplate({
|
|
|
16522
16524
|
contentComponentName
|
|
16523
16525
|
}) {
|
|
16524
16526
|
return ` <TabsContent value="${tabName}" className="h-full min-h-0 w-full flex-1">
|
|
16525
|
-
|
|
16526
|
-
<${contentComponentName} state={entityForm} tab="${tabName}" />
|
|
16527
|
-
</Form>
|
|
16527
|
+
<${contentComponentName} state={entityForm} tab="${tabName}" />
|
|
16528
16528
|
</TabsContent>`;
|
|
16529
16529
|
}
|
|
16530
16530
|
function renderEntityFormBody({ contentComponentName }) {
|
|
16531
16531
|
return ` <main className="w-full p-4 h-full">
|
|
16532
|
-
|
|
16533
|
-
<${contentComponentName} state={entityForm} />
|
|
16534
|
-
</Form>
|
|
16532
|
+
<${contentComponentName} state={entityForm} />
|
|
16535
16533
|
</main>`;
|
|
16536
16534
|
}
|
|
16537
16535
|
function renderEntityFormPage({
|
|
@@ -16557,7 +16555,6 @@ function renderEntityFormPage({
|
|
|
16557
16555
|
${showMetadataWidgetScrollThresholdImport}
|
|
16558
16556
|
${topLevelTabIconNamesLucide}
|
|
16559
16557
|
import { Button } from '@admin/components/ui/button'
|
|
16560
|
-
import { Form } from '@admin/components/ui/form'
|
|
16561
16558
|
${topLevelTabsFieldTabs}import { PageHeader } from '@admin/components/shared/page-header'${showMetadataWidgetEntityMetadataImport}
|
|
16562
16559
|
import type { ${singular} } from '@admin/actions/${actionImportPath}'
|
|
16563
16560
|
${showMetadataWidgetAdmin}
|
|
@@ -16718,7 +16715,8 @@ function buildEntityFormArtifacts({
|
|
|
16718
16715
|
});
|
|
16719
16716
|
const shellBody = topLevelTabsField ? topLevelTabsShell : defaultShellBody;
|
|
16720
16717
|
const topLevelTabComponentImports = topLevelTabComponents.map((spec) => `import { ${spec.componentName} } from './${spec.fileBaseName}'`).join("\n");
|
|
16721
|
-
const contentImports = topLevelTabsField ? `import
|
|
16718
|
+
const contentImports = topLevelTabsField ? `import { Form } from '@admin/components/ui/form'
|
|
16719
|
+
import type { TabValue, ${hookName} } from '${hookImportPath}'${topLevelTabComponentImports ? `
|
|
16722
16720
|
${topLevelTabComponentImports}` : ""}` : renderEntityFormContentRenderer({
|
|
16723
16721
|
kind: "renderFormContentLucideTemplate",
|
|
16724
16722
|
props: {
|
|
@@ -18705,6 +18703,7 @@ ${indent} ) : null}`;
|
|
|
18705
18703
|
function renderMultiSelectFieldActionButtonsSerializedValueTemplate({
|
|
18706
18704
|
indent,
|
|
18707
18705
|
fieldName,
|
|
18706
|
+
optionsId,
|
|
18708
18707
|
optionsDeclaration,
|
|
18709
18708
|
createState,
|
|
18710
18709
|
previewLimit,
|
|
@@ -18747,6 +18746,7 @@ ${indent} type="button"
|
|
|
18747
18746
|
${indent} variant="outline"
|
|
18748
18747
|
${indent} role="combobox"
|
|
18749
18748
|
${indent} aria-expanded={${openVar}}
|
|
18749
|
+
${indent} aria-controls="${optionsId}"
|
|
18750
18750
|
${indent} className={cn("w-full justify-between gap-2", {
|
|
18751
18751
|
${indent} "text-muted-foreground": selectedOptions.length === 0
|
|
18752
18752
|
${indent} })}
|
|
@@ -18788,7 +18788,7 @@ ${indent} </Button>
|
|
|
18788
18788
|
${indent} </FormControl>
|
|
18789
18789
|
${indent} }
|
|
18790
18790
|
${indent} />
|
|
18791
|
-
${indent} <PopoverContent className="w-(--anchor-width) p-0" align="start">
|
|
18791
|
+
${indent} <PopoverContent id="${optionsId}" className="w-(--anchor-width) p-0" align="start">
|
|
18792
18792
|
${indent} <Command>
|
|
18793
18793
|
${indent} ${searchInput}
|
|
18794
18794
|
${indent} <CommandList>
|
|
@@ -18858,6 +18858,7 @@ function renderMultiSelectField(field, indent, label, hintJSX, context) {
|
|
|
18858
18858
|
const placeholder = field.placeholder || `Select ${label.toLowerCase()}`;
|
|
18859
18859
|
const previewLimit = Math.max(1, Math.floor(field.previewLimit ?? 1));
|
|
18860
18860
|
const lowerLabel = label.toLowerCase();
|
|
18861
|
+
const optionsId = `${context.schemaName ? `${toKebabCase(singularize(context.schemaName))}-` : ""}${toKebabCase(field.name)}-options`;
|
|
18861
18862
|
const optionsDeclaration = isCreatable ? `const options = ${optionsVar}` : renderMultiSelectFieldRenderer({
|
|
18862
18863
|
kind: "renderMultiSelectFieldStaticOptionsTemplate",
|
|
18863
18864
|
props: {
|
|
@@ -18896,6 +18897,7 @@ ${indent} ${setSearchVar}('')` : "";
|
|
|
18896
18897
|
props: {
|
|
18897
18898
|
indent,
|
|
18898
18899
|
fieldName: field.name,
|
|
18900
|
+
optionsId,
|
|
18899
18901
|
optionsDeclaration,
|
|
18900
18902
|
createState,
|
|
18901
18903
|
previewLimit,
|
|
@@ -19210,6 +19212,7 @@ function buildEntityFormLayout({
|
|
|
19210
19212
|
(tabField) => generateFieldJSX2(tabField, `${cardFieldIndent} `, {
|
|
19211
19213
|
creatableSelectFieldNames,
|
|
19212
19214
|
defaultValueFromSourceNames,
|
|
19215
|
+
schemaName,
|
|
19213
19216
|
...childContext
|
|
19214
19217
|
})
|
|
19215
19218
|
).filter(Boolean).join("\n");
|
|
@@ -19256,6 +19259,7 @@ function buildEntityFormLayout({
|
|
|
19256
19259
|
return generateFieldJSX2(field, cardFieldIndent, {
|
|
19257
19260
|
creatableSelectFieldNames,
|
|
19258
19261
|
defaultValueFromSourceNames,
|
|
19262
|
+
schemaName,
|
|
19259
19263
|
slotFilter: "main"
|
|
19260
19264
|
});
|
|
19261
19265
|
};
|
|
@@ -19263,7 +19267,8 @@ function buildEntityFormLayout({
|
|
|
19263
19267
|
if (field.type === "tabs") return renderNestedTabsField(field);
|
|
19264
19268
|
return generateFieldJSX2(field, cardFieldIndent, {
|
|
19265
19269
|
creatableSelectFieldNames,
|
|
19266
|
-
defaultValueFromSourceNames
|
|
19270
|
+
defaultValueFromSourceNames,
|
|
19271
|
+
schemaName
|
|
19267
19272
|
});
|
|
19268
19273
|
};
|
|
19269
19274
|
const renderConditionalSidebarEntry = ({
|
|
@@ -19371,6 +19376,7 @@ function buildEntityFormLayout({
|
|
|
19371
19376
|
(field) => generateFieldJSX2(field, cardFieldIndent, {
|
|
19372
19377
|
creatableSelectFieldNames,
|
|
19373
19378
|
defaultValueFromSourceNames,
|
|
19379
|
+
schemaName,
|
|
19374
19380
|
slotFilter: "main"
|
|
19375
19381
|
})
|
|
19376
19382
|
).filter(Boolean).join("\n");
|
|
@@ -19705,7 +19711,7 @@ function generateForm(schema, pagesDir, options = {}) {
|
|
|
19705
19711
|
hasNestedObjectList,
|
|
19706
19712
|
hasCardContent: true,
|
|
19707
19713
|
hasSectionCardHeading,
|
|
19708
|
-
hasFormRoot:
|
|
19714
|
+
hasFormRoot: true,
|
|
19709
19715
|
hasButton: hasRelationship || hasSelectCombobox || hasNestedList,
|
|
19710
19716
|
hasFormControls: contentUsesFormControls || contentHasRenderedIconPostfix,
|
|
19711
19717
|
hasInput: contentUsesInput
|
|
@@ -22400,37 +22406,24 @@ function renderSingleFormImportsTemplate({ map }) {
|
|
|
22400
22406
|
${map}
|
|
22401
22407
|
} from '@admin/components/ui/card'`;
|
|
22402
22408
|
}
|
|
22403
|
-
function
|
|
22404
|
-
|
|
22405
|
-
|
|
22406
|
-
|
|
22407
|
-
|
|
22408
|
-
|
|
22409
|
-
|
|
22410
|
-
|
|
22411
|
-
|
|
22409
|
+
function renderSingleFormCardModuleTemplate({
|
|
22410
|
+
imports,
|
|
22411
|
+
content
|
|
22412
|
+
}) {
|
|
22413
|
+
return `'use client'
|
|
22414
|
+
${imports}
|
|
22415
|
+
|
|
22416
|
+
${content}
|
|
22417
|
+
`;
|
|
22418
|
+
}
|
|
22419
|
+
function renderSingleFormEntryModuleTemplate({
|
|
22412
22420
|
singular,
|
|
22413
|
-
hasCreatableSelectContent,
|
|
22414
22421
|
actionImportPath,
|
|
22415
|
-
|
|
22416
|
-
joinText,
|
|
22422
|
+
cardImports,
|
|
22417
22423
|
cardRenders
|
|
22418
22424
|
}) {
|
|
22419
|
-
return `
|
|
22420
|
-
${
|
|
22421
|
-
import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
|
|
22422
|
-
import { useMutation, ${hasCreatableSelectUseQuery}useQueryClient } from '@tanstack/react-query'${lucideIconsLucide}
|
|
22423
|
-
import {${hasNestedListUseFieldArray} useForm } from 'react-hook-form'
|
|
22424
|
-
import { toast } from 'sonner'
|
|
22425
|
-
import { z } from 'zod/v3'${hasTabsFieldImport}${hasRelationshipImport}
|
|
22426
|
-
${relHookImportsContent}${join}
|
|
22427
|
-
import type {
|
|
22428
|
-
${singular},
|
|
22429
|
-
${hasCreatableSelectContent}${singular}UpsertInput
|
|
22430
|
-
} from '@admin/actions/${actionImportPath}'
|
|
22431
|
-
import { ${hasCreatableSelectContentText}upsert${singular} } from '@admin/actions/${actionImportPath}'
|
|
22432
|
-
|
|
22433
|
-
${joinText}
|
|
22425
|
+
return `import type { ${singular} } from '@admin/actions/${actionImportPath}'
|
|
22426
|
+
${cardImports}
|
|
22434
22427
|
|
|
22435
22428
|
interface ${singular}FormProps {
|
|
22436
22429
|
initialData?: ${singular} | null
|
|
@@ -22449,8 +22442,10 @@ function renderSingleFormRenderer(request) {
|
|
|
22449
22442
|
switch (request.kind) {
|
|
22450
22443
|
case "renderSingleFormImportsTemplate":
|
|
22451
22444
|
return renderSingleFormImportsTemplate(request.props);
|
|
22452
|
-
case "
|
|
22453
|
-
return
|
|
22445
|
+
case "renderSingleFormCardModuleTemplate":
|
|
22446
|
+
return renderSingleFormCardModuleTemplate(request.props);
|
|
22447
|
+
case "renderSingleFormEntryModuleTemplate":
|
|
22448
|
+
return renderSingleFormEntryModuleTemplate(request.props);
|
|
22454
22449
|
}
|
|
22455
22450
|
throw new Error("Unsupported renderer request");
|
|
22456
22451
|
}
|
|
@@ -22536,12 +22531,12 @@ ${zodFields}
|
|
|
22536
22531
|
})
|
|
22537
22532
|
${tabParserConst}
|
|
22538
22533
|
|
|
22539
|
-
function ${groupComponentName}({ initialData }: { initialData?: ${singular} | null }) {
|
|
22534
|
+
export function ${groupComponentName}({ initialData }: { initialData?: ${singular} | null }) {
|
|
22540
22535
|
const queryClient = useQueryClient()${analysisHasTabsFieldTab}${relStateContent}${creatableSelectStateContent}${selectStateContent}
|
|
22541
22536
|
|
|
22542
22537
|
const form = useForm<z.infer<typeof ${groupVarPrefix}Schema>>({
|
|
22543
22538
|
resolver: standardSchemaResolver(${groupVarPrefix}Schema),
|
|
22544
|
-
mode: '
|
|
22539
|
+
mode: 'onChange',
|
|
22545
22540
|
reValidateMode: 'onChange',
|
|
22546
22541
|
defaultValues: {
|
|
22547
22542
|
${defaultValues}
|
|
@@ -22571,7 +22566,7 @@ ${fieldArrayHooksContent}
|
|
|
22571
22566
|
${creatableSelectHandlersContent}
|
|
22572
22567
|
return (
|
|
22573
22568
|
<Form {...form}>
|
|
22574
|
-
<form onSubmit={form.handleSubmit((values) => {
|
|
22569
|
+
<form autoComplete="off" onSubmit={form.handleSubmit((values) => {
|
|
22575
22570
|
mutation.mutate(values as ${singular}UpsertInput)
|
|
22576
22571
|
})}>
|
|
22577
22572
|
<Card >
|
|
@@ -22805,7 +22800,7 @@ function renderSingleFormBuildGroupFieldsJsxRenderer(request) {
|
|
|
22805
22800
|
}
|
|
22806
22801
|
|
|
22807
22802
|
// adapters/next/generators/form/form-single/build-group-fields-jsx.ts
|
|
22808
|
-
function buildGroupFieldsJSX(group2, analysis, defaultValueFromSourceNames, creatableSelectFieldNames) {
|
|
22803
|
+
function buildGroupFieldsJSX(group2, analysis, defaultValueFromSourceNames, creatableSelectFieldNames, schemaName) {
|
|
22809
22804
|
const indent = " ";
|
|
22810
22805
|
return group2.fields.map((f) => {
|
|
22811
22806
|
if (f.type === "tabs" && f.tabs) {
|
|
@@ -22814,7 +22809,8 @@ function buildGroupFieldsJSX(group2, analysis, defaultValueFromSourceNames, crea
|
|
|
22814
22809
|
const tabFields = (t.fields || []).map(
|
|
22815
22810
|
(tf) => generateFieldJSX2(tf, " ", {
|
|
22816
22811
|
creatableSelectFieldNames,
|
|
22817
|
-
defaultValueFromSourceNames
|
|
22812
|
+
defaultValueFromSourceNames,
|
|
22813
|
+
schemaName
|
|
22818
22814
|
})
|
|
22819
22815
|
).join("\n");
|
|
22820
22816
|
return renderSingleFormBuildGroupFieldsJsxRenderer({
|
|
@@ -22838,7 +22834,8 @@ function buildGroupFieldsJSX(group2, analysis, defaultValueFromSourceNames, crea
|
|
|
22838
22834
|
if (analysis.tabFieldNames.has(f.name)) return "";
|
|
22839
22835
|
return generateFieldJSX2(f, indent, {
|
|
22840
22836
|
creatableSelectFieldNames,
|
|
22841
|
-
defaultValueFromSourceNames
|
|
22837
|
+
defaultValueFromSourceNames,
|
|
22838
|
+
schemaName
|
|
22842
22839
|
});
|
|
22843
22840
|
}).filter(Boolean).join("\n");
|
|
22844
22841
|
}
|
|
@@ -22880,7 +22877,8 @@ function generateCardComponent(group2, schema, analysis, allFlatFields) {
|
|
|
22880
22877
|
group2,
|
|
22881
22878
|
analysis,
|
|
22882
22879
|
defaultValueFromSourceNames,
|
|
22883
|
-
creatableSelectFieldNames
|
|
22880
|
+
creatableSelectFieldNames,
|
|
22881
|
+
schema.name
|
|
22884
22882
|
);
|
|
22885
22883
|
const relState = buildGroupRelState(analysis);
|
|
22886
22884
|
const selectState = buildGroupSelectState(
|
|
@@ -23043,135 +23041,179 @@ function generateSingleForm(schema, pagesDir, options = {}) {
|
|
|
23043
23041
|
(f) => !f.primaryKey && f.name !== "createdAt" && f.name !== "updatedAt" && !isAuthorshipFieldName(f.name)
|
|
23044
23042
|
);
|
|
23045
23043
|
const cardGroups = parseCardGroups(allFormFields, schema.label);
|
|
23046
|
-
const hasBoolean = hasFieldType(schema.fields, "boolean");
|
|
23047
|
-
const hasImage = hasFieldType(schema.fields, "image");
|
|
23048
|
-
const hasVideo = hasFieldType(schema.fields, "video");
|
|
23049
|
-
const hasMedia = hasFieldType(schema.fields, "media");
|
|
23050
|
-
const hasGallery = hasFieldType(schema.fields, "gallery");
|
|
23051
|
-
const hasIcon = hasFieldType(schema.fields, "icon");
|
|
23052
|
-
const hasIconPostfix = hasRenderedIconPostfix(allFormFields);
|
|
23053
|
-
const hasDate = hasFieldType(schema.fields, "date");
|
|
23054
23044
|
const flatFields = flattenFields(allFormFields);
|
|
23055
|
-
const
|
|
23056
|
-
|
|
23057
|
-
|
|
23058
|
-
|
|
23059
|
-
|
|
23060
|
-
|
|
23061
|
-
|
|
23062
|
-
|
|
23063
|
-
|
|
23064
|
-
|
|
23065
|
-
|
|
23066
|
-
|
|
23067
|
-
|
|
23068
|
-
|
|
23069
|
-
|
|
23070
|
-
|
|
23071
|
-
|
|
23072
|
-
|
|
23073
|
-
)
|
|
23074
|
-
|
|
23075
|
-
|
|
23076
|
-
|
|
23077
|
-
|
|
23078
|
-
|
|
23079
|
-
|
|
23080
|
-
|
|
23081
|
-
|
|
23045
|
+
const inputFreeFieldTypes = /* @__PURE__ */ new Set([
|
|
23046
|
+
"boolean",
|
|
23047
|
+
"image",
|
|
23048
|
+
"video",
|
|
23049
|
+
"media",
|
|
23050
|
+
"gallery",
|
|
23051
|
+
"icon",
|
|
23052
|
+
"date",
|
|
23053
|
+
"select",
|
|
23054
|
+
"richtext",
|
|
23055
|
+
"markdown",
|
|
23056
|
+
"text",
|
|
23057
|
+
"relationship",
|
|
23058
|
+
"list",
|
|
23059
|
+
"separator",
|
|
23060
|
+
"tabs"
|
|
23061
|
+
]);
|
|
23062
|
+
const formControlFreeFieldTypes = /* @__PURE__ */ new Set(["separator", "tabs"]);
|
|
23063
|
+
const cardArtifacts = cardGroups.map((group2) => {
|
|
23064
|
+
const analysis = analyzeGroup(group2.fields);
|
|
23065
|
+
const groupFlatFields = group2.flatFields;
|
|
23066
|
+
const creatableSelectFields = groupFlatFields.filter(
|
|
23067
|
+
(field) => field.type === "select" && field.creatable === true
|
|
23068
|
+
);
|
|
23069
|
+
const hasCreatableSelect = creatableSelectFields.length > 0;
|
|
23070
|
+
const hasMultiSelect = analysis.multiSelectFields.length > 0;
|
|
23071
|
+
const hasRelationship = analysis.relFields.length > 0;
|
|
23072
|
+
const hasSelectCombobox = hasRelationship || hasMultiSelect || hasCreatableSelect;
|
|
23073
|
+
const hasSelect = groupFlatFields.some(
|
|
23074
|
+
(field) => field.type === "select" && field.multiple !== true && field.creatable !== true
|
|
23075
|
+
);
|
|
23076
|
+
const hasIconPostfix = hasRenderedIconPostfix(group2.fields);
|
|
23077
|
+
const hasNestedList = analysis.listFieldsWithNested.length > 0;
|
|
23078
|
+
const hasInput = analysis.listFieldsWithNested.some(
|
|
23079
|
+
(field) => field.fields?.some(
|
|
23080
|
+
(nestedField) => !["boolean", "image", "video", "media"].includes(nestedField.type)
|
|
23081
|
+
)
|
|
23082
|
+
) || groupFlatFields.some(
|
|
23083
|
+
(field) => !field.primaryKey && !field.hidden && !inputFreeFieldTypes.has(field.type)
|
|
23084
|
+
);
|
|
23085
|
+
const hasFormControls = groupFlatFields.some((field) => {
|
|
23086
|
+
if (field.primaryKey || field.hidden) return false;
|
|
23087
|
+
if (field.hasIcon && !["group", "section", "tabs", "separator"].includes(field.type)) {
|
|
23088
|
+
return true;
|
|
23082
23089
|
}
|
|
23090
|
+
if (field.type === "list" && (!field.fields || field.fields.length === 0)) return false;
|
|
23091
|
+
return !formControlFreeFieldTypes.has(field.type);
|
|
23092
|
+
}) || hasIconPostfix;
|
|
23093
|
+
const groupUiImports = buildUiImports({
|
|
23094
|
+
hasBoolean: hasFieldType(group2.fields, "boolean"),
|
|
23095
|
+
hasTextarea: hasFieldType(group2.fields, "text"),
|
|
23096
|
+
hasImage: hasFieldType(group2.fields, "image"),
|
|
23097
|
+
hasVideo: hasFieldType(group2.fields, "video"),
|
|
23098
|
+
hasMedia: hasFieldType(group2.fields, "media"),
|
|
23099
|
+
hasGallery: hasFieldType(group2.fields, "gallery"),
|
|
23100
|
+
hasIcon: hasFieldType(group2.fields, "icon"),
|
|
23101
|
+
hasIconPostfix,
|
|
23102
|
+
hasDate: hasFieldType(group2.fields, "date"),
|
|
23103
|
+
hasMarkdown: hasFieldType(group2.fields, "markdown"),
|
|
23104
|
+
hasRichtext: hasFieldType(group2.fields, "richtext"),
|
|
23105
|
+
hasSeparator: hasFieldType(group2.fields, "separator"),
|
|
23106
|
+
hasSelect,
|
|
23107
|
+
hasSelectCombobox,
|
|
23108
|
+
hasCreatableSelect,
|
|
23109
|
+
hasMultiSelect,
|
|
23110
|
+
hasTabsField: analysis.hasTabsField,
|
|
23111
|
+
hasRelationship,
|
|
23112
|
+
hasSimpleList: hasSimpleListField(group2.fields),
|
|
23113
|
+
hasNestedList,
|
|
23114
|
+
hasNestedObjectList: analysis.hasNestedObjectList,
|
|
23115
|
+
hasCardContent: true,
|
|
23116
|
+
hasSectionCardHeading: true,
|
|
23117
|
+
hasFormRoot: true,
|
|
23118
|
+
hasButton: true,
|
|
23119
|
+
hasFormControls,
|
|
23120
|
+
hasInput
|
|
23121
|
+
});
|
|
23122
|
+
const cardImportIndex = groupUiImports.findIndex(
|
|
23123
|
+
(value) => value.includes("from '@admin/components/ui/card'")
|
|
23124
|
+
);
|
|
23125
|
+
if (cardImportIndex !== -1) {
|
|
23126
|
+
const cardImportNames = [
|
|
23127
|
+
"Card",
|
|
23128
|
+
"CardContent",
|
|
23129
|
+
...group2.description ? ["CardDescription"] : [],
|
|
23130
|
+
"CardFooter",
|
|
23131
|
+
"CardHeader",
|
|
23132
|
+
"CardTitle"
|
|
23133
|
+
];
|
|
23134
|
+
groupUiImports[cardImportIndex] = renderSingleFormRenderer({
|
|
23135
|
+
kind: "renderSingleFormImportsTemplate",
|
|
23136
|
+
props: {
|
|
23137
|
+
map: cardImportNames.map((name) => ` ${name}`).join(",\n")
|
|
23138
|
+
}
|
|
23139
|
+
});
|
|
23083
23140
|
}
|
|
23084
|
-
|
|
23085
|
-
|
|
23086
|
-
|
|
23087
|
-
|
|
23088
|
-
|
|
23089
|
-
|
|
23090
|
-
|
|
23091
|
-
|
|
23092
|
-
|
|
23093
|
-
|
|
23094
|
-
|
|
23095
|
-
|
|
23096
|
-
|
|
23097
|
-
|
|
23098
|
-
|
|
23099
|
-
|
|
23100
|
-
|
|
23101
|
-
hasCreatableSelect
|
|
23102
|
-
|
|
23103
|
-
|
|
23104
|
-
|
|
23105
|
-
|
|
23106
|
-
|
|
23107
|
-
|
|
23108
|
-
|
|
23109
|
-
|
|
23110
|
-
|
|
23111
|
-
|
|
23112
|
-
|
|
23113
|
-
|
|
23114
|
-
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23141
|
+
groupUiImports.push("import { Spinner } from '@admin/components/ui/spinner'");
|
|
23142
|
+
const lucideIcons = [];
|
|
23143
|
+
const addLucideIcons = (...icons) => {
|
|
23144
|
+
for (const icon of icons) {
|
|
23145
|
+
if (!lucideIcons.includes(icon)) lucideIcons.push(icon);
|
|
23146
|
+
}
|
|
23147
|
+
};
|
|
23148
|
+
addLucideIcons(...collectTabIconNames(group2.fields));
|
|
23149
|
+
if (hasSelectCombobox) addLucideIcons("Check", "ChevronsUpDown");
|
|
23150
|
+
if (hasCreatableSelect || hasNestedList) addLucideIcons("Plus");
|
|
23151
|
+
if (hasNestedList) addLucideIcons("X");
|
|
23152
|
+
const relationshipImports = Array.from(
|
|
23153
|
+
new Set(analysis.relFields.map((field) => field.relationship).filter(Boolean))
|
|
23154
|
+
).map((relationship) => {
|
|
23155
|
+
const relationshipPlural = toPascalCase(pluralize(relationship || ""));
|
|
23156
|
+
return `import { use${relationshipPlural} } from '@admin/hooks/use-${relationship}'`;
|
|
23157
|
+
}).join("\n");
|
|
23158
|
+
const needsReact = hasRelationship || hasMultiSelect || hasCreatableSelect || analysis.listFieldsWithNested.length > 0;
|
|
23159
|
+
const hookImports = ["useForm"];
|
|
23160
|
+
if (hasNestedList) hookImports.unshift("useFieldArray");
|
|
23161
|
+
const actionTypeImports = [
|
|
23162
|
+
Singular,
|
|
23163
|
+
...hasCreatableSelect ? [`${Singular}SelectOption`] : [],
|
|
23164
|
+
`${Singular}UpsertInput`
|
|
23165
|
+
];
|
|
23166
|
+
const actionImports = [
|
|
23167
|
+
...hasCreatableSelect ? [`create${Singular}SelectOption`, `get${Singular}SelectOptions`] : [],
|
|
23168
|
+
`upsert${Singular}`
|
|
23169
|
+
];
|
|
23170
|
+
const cardImports = [
|
|
23171
|
+
needsReact ? "import * as React from 'react'" : "",
|
|
23172
|
+
"import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'",
|
|
23173
|
+
`import { useMutation, ${hasCreatableSelect ? "useQuery, " : ""}useQueryClient } from '@tanstack/react-query'`,
|
|
23174
|
+
lucideIcons.length > 0 ? `import { ${lucideIcons.join(", ")} } from 'lucide-react'` : "",
|
|
23175
|
+
`import { ${hookImports.join(", ")} } from 'react-hook-form'`,
|
|
23176
|
+
"import { toast } from 'sonner'",
|
|
23177
|
+
"import { z } from 'zod/v3'",
|
|
23178
|
+
analysis.hasTabsField ? "import { parseAsStringLiteral, useQueryState } from 'nuqs'" : "",
|
|
23179
|
+
hasSelectCombobox ? "import { cn } from '@admin/utils/shared/cn'" : "",
|
|
23180
|
+
relationshipImports,
|
|
23181
|
+
groupUiImports.join("\n"),
|
|
23182
|
+
`import type { ${actionTypeImports.join(",\n ")} } from '@admin/actions/${actionImportPath}'`,
|
|
23183
|
+
`import { ${actionImports.join(", ")} } from '@admin/actions/${actionImportPath}'`
|
|
23184
|
+
].filter(Boolean).join("\n");
|
|
23185
|
+
const content2 = renderSingleFormRenderer({
|
|
23186
|
+
kind: "renderSingleFormCardModuleTemplate",
|
|
23124
23187
|
props: {
|
|
23125
|
-
|
|
23188
|
+
imports: cardImports,
|
|
23189
|
+
content: generateCardComponent(group2, schema, analysis, flatFields)
|
|
23126
23190
|
}
|
|
23127
|
-
})
|
|
23128
|
-
|
|
23129
|
-
|
|
23130
|
-
|
|
23131
|
-
|
|
23132
|
-
|
|
23133
|
-
if (!lucideIcons.includes(icon)) lucideIcons.push(icon);
|
|
23134
|
-
}
|
|
23135
|
-
};
|
|
23136
|
-
addLucideIcons(...tabIconNames);
|
|
23137
|
-
if (hasRelationship || hasSelectCombobox) addLucideIcons("Check", "ChevronsUpDown");
|
|
23138
|
-
if (hasCreatableSelect) addLucideIcons("Plus");
|
|
23139
|
-
if (hasNestedList) {
|
|
23140
|
-
addLucideIcons("Plus", "X");
|
|
23141
|
-
}
|
|
23142
|
-
const relHookImports = allRelFields.map((f) => {
|
|
23143
|
-
const relPlural = toPascalCase(pluralize(f.relationship || ""));
|
|
23144
|
-
return `import { use${relPlural} } from '@admin/hooks/use-${f.relationship}'`;
|
|
23145
|
-
}).filter((v, i, a) => a.indexOf(v) === i).join("\n");
|
|
23146
|
-
const cardComponents = cardGroups.map((group2) => {
|
|
23147
|
-
const analysis = analyzeGroup(group2.fields);
|
|
23148
|
-
return generateCardComponent(group2, schema, analysis, flatFields);
|
|
23191
|
+
});
|
|
23192
|
+
return {
|
|
23193
|
+
content: content2,
|
|
23194
|
+
fileName: `${toKebabCase(group2.componentName)}.tsx`,
|
|
23195
|
+
group: group2
|
|
23196
|
+
};
|
|
23149
23197
|
});
|
|
23150
|
-
const cardRenders =
|
|
23198
|
+
const cardRenders = cardArtifacts.map(({ group: group2 }) => ` <${group2.componentName} initialData={initialData} />`).join("\n");
|
|
23151
23199
|
const content = renderSingleFormRenderer({
|
|
23152
|
-
kind: "
|
|
23200
|
+
kind: "renderSingleFormEntryModuleTemplate",
|
|
23153
23201
|
props: {
|
|
23154
|
-
needsReactImport: needsReact ? "\nimport * as React from 'react'" : "",
|
|
23155
|
-
hasCreatableSelectUseQuery: hasCreatableSelect ? "useQuery, " : "",
|
|
23156
|
-
lucideIconsLucide: lucideIcons.length > 0 ? `
|
|
23157
|
-
import { ${lucideIcons.join(", ")} } from 'lucide-react'` : "",
|
|
23158
|
-
hasNestedListUseFieldArray: hasNestedList ? " useFieldArray," : "",
|
|
23159
|
-
hasTabsFieldImport: hasTabsField ? "\nimport { parseAsStringLiteral, useQueryState } from 'nuqs'" : "",
|
|
23160
|
-
hasRelationshipImport: hasRelationship || hasSelectCombobox ? "\nimport { cn } from '@admin/utils/shared/cn'" : "",
|
|
23161
|
-
relHookImportsContent: relHookImports ? `${relHookImports}
|
|
23162
|
-
` : "",
|
|
23163
|
-
join: uiImports.join("\n"),
|
|
23164
23202
|
singular: Singular,
|
|
23165
|
-
hasCreatableSelectContent: hasCreatableSelect ? `${Singular}SelectOption,
|
|
23166
|
-
` : "",
|
|
23167
23203
|
actionImportPath,
|
|
23168
|
-
|
|
23169
|
-
|
|
23204
|
+
cardImports: cardArtifacts.map(
|
|
23205
|
+
({ fileName, group: group2 }) => `import { ${group2.componentName} } from './${fileName.slice(0, -4)}'`
|
|
23206
|
+
).join("\n"),
|
|
23170
23207
|
cardRenders
|
|
23171
23208
|
}
|
|
23172
23209
|
});
|
|
23173
23210
|
return {
|
|
23174
|
-
files: [
|
|
23211
|
+
files: [
|
|
23212
|
+
createGeneratedFile(`${pagesDir}/${schema.name}/${schema.name}-form.tsx`, content),
|
|
23213
|
+
...cardArtifacts.map(
|
|
23214
|
+
({ content: cardContent, fileName }) => createGeneratedFile(`${pagesDir}/${schema.name}/${fileName}`, cardContent)
|
|
23215
|
+
)
|
|
23216
|
+
]
|
|
23175
23217
|
};
|
|
23176
23218
|
}
|
|
23177
23219
|
|