@betterstart/cli 0.1.70 → 0.1.72
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/cli.js
CHANGED
|
@@ -84,7 +84,7 @@ function walkFields(fields, callback, options = DEFAULT_WALK_OPTIONS) {
|
|
|
84
84
|
function walk(fieldsToWalk, depth, parent) {
|
|
85
85
|
for (const field of fieldsToWalk) {
|
|
86
86
|
callback(field, depth, parent);
|
|
87
|
-
if (options.includeGroups && field.type === "group" && field.fields) {
|
|
87
|
+
if (options.includeGroups && (field.type === "group" || field.type === "section") && field.fields) {
|
|
88
88
|
walk(field.fields, depth + 1, field);
|
|
89
89
|
}
|
|
90
90
|
if (options.includeLists && field.type === "list" && field.fields) {
|
|
@@ -140,7 +140,7 @@ function flattenFields(fields) {
|
|
|
140
140
|
function flattenFieldsWithoutIdCheck(fields) {
|
|
141
141
|
const flattened = [];
|
|
142
142
|
for (const field of fields) {
|
|
143
|
-
if (field.type === "group" && field.fields) {
|
|
143
|
+
if ((field.type === "group" || field.type === "section") && field.fields) {
|
|
144
144
|
flattened.push(...flattenFieldsWithoutIdCheck(field.fields));
|
|
145
145
|
} else if (field.type === "tabs" && field.tabs) {
|
|
146
146
|
for (const tab of field.tabs) {
|
|
@@ -3353,12 +3353,12 @@ var FIELD_TYPES = {
|
|
|
3353
3353
|
TABS: "tabs",
|
|
3354
3354
|
LIST: "list",
|
|
3355
3355
|
SEPARATOR: "separator",
|
|
3356
|
-
|
|
3356
|
+
SECTION: "section",
|
|
3357
3357
|
SELECT: "select",
|
|
3358
3358
|
RELATIONSHIP: "relationship",
|
|
3359
3359
|
CURRICULUM: "curriculum"
|
|
3360
3360
|
};
|
|
3361
|
-
var LAYOUT_FIELD_TYPES = [FIELD_TYPES.SEPARATOR
|
|
3361
|
+
var LAYOUT_FIELD_TYPES = [FIELD_TYPES.SEPARATOR];
|
|
3362
3362
|
var NESTED_FIELD_TYPES = [FIELD_TYPES.GROUP, FIELD_TYPES.TABS, FIELD_TYPES.LIST];
|
|
3363
3363
|
var RICH_TEXT_FIELD_TYPES = [FIELD_TYPES.MARKDOWN, FIELD_TYPES.TEXT];
|
|
3364
3364
|
var LONG_TEXT_FIELD_TYPES = [
|
|
@@ -6139,7 +6139,7 @@ function generateFieldJSXCore(field, indent = " ") {
|
|
|
6139
6139
|
const fieldType = getFormFieldType(field);
|
|
6140
6140
|
const label = field.label || field.name;
|
|
6141
6141
|
const hintJSX = field.hint ? `${indent} <FormDescription>${field.hint}</FormDescription>` : "";
|
|
6142
|
-
if (field.type === "group" && field.fields) return renderGroupField(field, indent, label);
|
|
6142
|
+
if ((field.type === "group" || field.type === "section") && field.fields) return renderGroupField(field, indent, label);
|
|
6143
6143
|
if (field.type === "separator") return renderSeparatorField(field, indent);
|
|
6144
6144
|
if (field.type === "boolean") return renderBooleanField(field, indent, label, hintJSX);
|
|
6145
6145
|
if (field.type === "image") return renderImageField(field, indent, label, hintJSX);
|
|
@@ -6969,6 +6969,184 @@ ${hasDraft ? ` <Button
|
|
|
6969
6969
|
// src/generators/form/form-single.ts
|
|
6970
6970
|
import fs21 from "fs";
|
|
6971
6971
|
import path21 from "path";
|
|
6972
|
+
function parseCardGroups(allFormFields, schemaLabel) {
|
|
6973
|
+
const sections = allFormFields.filter((f) => f.type === "section" && f.fields);
|
|
6974
|
+
const nonSections = allFormFields.filter((f) => f.type !== "section" && !isLayoutField(f.type));
|
|
6975
|
+
if (sections.length === 0) {
|
|
6976
|
+
const title = nonSections[0]?.label || nonSections[0]?.name || schemaLabel;
|
|
6977
|
+
return [
|
|
6978
|
+
{
|
|
6979
|
+
title,
|
|
6980
|
+
description: "",
|
|
6981
|
+
varPrefix: toCamelCase(nonSections[0]?.name || "form"),
|
|
6982
|
+
componentName: `${toPascalCase(nonSections[0]?.name || "form")}Card`,
|
|
6983
|
+
fields: nonSections,
|
|
6984
|
+
flatFields: flattenFields(nonSections).filter((f) => !isLayoutField(f.type))
|
|
6985
|
+
}
|
|
6986
|
+
];
|
|
6987
|
+
}
|
|
6988
|
+
const groups = [];
|
|
6989
|
+
for (const section of sections) {
|
|
6990
|
+
const innerFields = section.fields || [];
|
|
6991
|
+
groups.push({
|
|
6992
|
+
title: section.label || section.name,
|
|
6993
|
+
description: section.description || section.hint || "",
|
|
6994
|
+
varPrefix: toCamelCase(section.name),
|
|
6995
|
+
componentName: `${toPascalCase(section.name)}Card`,
|
|
6996
|
+
fields: innerFields,
|
|
6997
|
+
flatFields: flattenFields(innerFields).filter((f) => !isLayoutField(f.type))
|
|
6998
|
+
});
|
|
6999
|
+
}
|
|
7000
|
+
if (nonSections.length > 0) {
|
|
7001
|
+
const title = nonSections[0]?.label || nonSections[0]?.name || "General";
|
|
7002
|
+
groups.unshift({
|
|
7003
|
+
title,
|
|
7004
|
+
description: "",
|
|
7005
|
+
varPrefix: toCamelCase(nonSections[0]?.name || "general"),
|
|
7006
|
+
componentName: `${toPascalCase(nonSections[0]?.name || "General")}Card`,
|
|
7007
|
+
fields: nonSections,
|
|
7008
|
+
flatFields: flattenFields(nonSections).filter((f) => !isLayoutField(f.type))
|
|
7009
|
+
});
|
|
7010
|
+
}
|
|
7011
|
+
return groups;
|
|
7012
|
+
}
|
|
7013
|
+
function analyzeGroup(fields) {
|
|
7014
|
+
const tabFieldNames = /* @__PURE__ */ new Set();
|
|
7015
|
+
for (const f of fields) {
|
|
7016
|
+
if (f.type === "tabs" && f.tabs) {
|
|
7017
|
+
for (const tab of f.tabs) {
|
|
7018
|
+
if (tab.fields) for (const tf of tab.fields) tabFieldNames.add(tf.name);
|
|
7019
|
+
}
|
|
7020
|
+
}
|
|
7021
|
+
}
|
|
7022
|
+
const relFields = collectRelationshipFields(fields, tabFieldNames);
|
|
7023
|
+
const listFieldsWithNested = [];
|
|
7024
|
+
function collectLists(flds) {
|
|
7025
|
+
for (const f of flds) {
|
|
7026
|
+
if (f.type === "list" && f.fields && f.fields.length > 0 && !f.hidden) {
|
|
7027
|
+
listFieldsWithNested.push(f);
|
|
7028
|
+
}
|
|
7029
|
+
if (f.type === "group" && f.fields) collectLists(f.fields);
|
|
7030
|
+
if (f.type === "tabs" && f.tabs) {
|
|
7031
|
+
for (const tab of f.tabs) {
|
|
7032
|
+
if (tab.fields) collectLists(tab.fields);
|
|
7033
|
+
}
|
|
7034
|
+
}
|
|
7035
|
+
}
|
|
7036
|
+
}
|
|
7037
|
+
collectLists(fields);
|
|
7038
|
+
const hasTabsField = fields.some((f) => f.type === "tabs");
|
|
7039
|
+
const tabsField = fields.find((f) => f.type === "tabs");
|
|
7040
|
+
const firstTabName = tabsField?.tabs?.[0]?.name || "";
|
|
7041
|
+
return { relFields, listFieldsWithNested, hasTabsField, firstTabName, tabFieldNames };
|
|
7042
|
+
}
|
|
7043
|
+
function buildGroupFieldsJSX(group3, analysis, skipLabel) {
|
|
7044
|
+
const indent = " ";
|
|
7045
|
+
return group3.fields.map((f) => {
|
|
7046
|
+
if (f.type === "tabs" && f.tabs) {
|
|
7047
|
+
const tabsList = f.tabs.map((t) => ` <TabsTrigger value="${t.name}">${t.label}</TabsTrigger>`).join("\n");
|
|
7048
|
+
const tabsContent = f.tabs.map((t) => {
|
|
7049
|
+
const tabFields = (t.fields || []).map((tf) => generateFieldJSX2(tf, " ")).join("\n");
|
|
7050
|
+
return ` <TabsContent value="${t.name}" className="space-y-6">
|
|
7051
|
+
${tabFields}
|
|
7052
|
+
</TabsContent>`;
|
|
7053
|
+
}).join("\n");
|
|
7054
|
+
return `${indent}<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
|
7055
|
+
<TabsList>
|
|
7056
|
+
${tabsList}
|
|
7057
|
+
</TabsList>
|
|
7058
|
+
${tabsContent}
|
|
7059
|
+
${indent}</Tabs>`;
|
|
7060
|
+
}
|
|
7061
|
+
if (analysis.tabFieldNames.has(f.name)) return "";
|
|
7062
|
+
return generateFieldJSX2(f, indent, { skipLabel });
|
|
7063
|
+
}).filter(Boolean).join("\n");
|
|
7064
|
+
}
|
|
7065
|
+
function buildGroupRelState(analysis) {
|
|
7066
|
+
return analysis.relFields.map((f) => {
|
|
7067
|
+
const relPlural = toPascalCase(pluralize(f.relationship || ""));
|
|
7068
|
+
return ` const [${f.name}Open, set${toPascalCase(f.name)}Open] = React.useState(false)
|
|
7069
|
+
const { data: ${f.relationship}Data } = use${relPlural}()`;
|
|
7070
|
+
}).filter((v, i, a) => a.indexOf(v) === i).join("\n");
|
|
7071
|
+
}
|
|
7072
|
+
function buildGroupFieldArrayHooks(analysis) {
|
|
7073
|
+
return analysis.listFieldsWithNested.map((field) => {
|
|
7074
|
+
const pascalFieldName = toPascalCase(field.name);
|
|
7075
|
+
return ` const [${field.name}Expanded, set${pascalFieldName}Expanded] = React.useState<string | undefined>(undefined)
|
|
7076
|
+
const ${field.name}FieldArray = useFieldArray({
|
|
7077
|
+
control: form.control,
|
|
7078
|
+
name: '${field.name}'
|
|
7079
|
+
})`;
|
|
7080
|
+
}).join("\n");
|
|
7081
|
+
}
|
|
7082
|
+
function generateCardComponent(group3, schema, analysis) {
|
|
7083
|
+
const singular = singularize(schema.name);
|
|
7084
|
+
const Singular = toPascalCase(singular);
|
|
7085
|
+
const zodFields = buildZodFields2(group3.flatFields);
|
|
7086
|
+
const defaultValues = buildDefaultValues2(group3.flatFields);
|
|
7087
|
+
const isSingleField = group3.fields.length === 1 && group3.title === (group3.fields[0].label || group3.fields[0].name);
|
|
7088
|
+
const fieldsJSX = buildGroupFieldsJSX(group3, analysis, isSingleField);
|
|
7089
|
+
const relState = buildGroupRelState(analysis);
|
|
7090
|
+
const fieldArrayHooks = buildGroupFieldArrayHooks(analysis);
|
|
7091
|
+
const descriptionLine = group3.description ? `
|
|
7092
|
+
<CardDescription>${group3.description}</CardDescription>` : "";
|
|
7093
|
+
return `const ${group3.varPrefix}Schema = z.object({
|
|
7094
|
+
${zodFields}
|
|
7095
|
+
})
|
|
7096
|
+
|
|
7097
|
+
function ${group3.componentName}({ initialData }: { initialData?: ${Singular}Data | null }) {
|
|
7098
|
+
const queryClient = useQueryClient()${analysis.hasTabsField ? `
|
|
7099
|
+
const [activeTab, setActiveTab] = useQueryState('tab', { defaultValue: '${analysis.firstTabName}' })` : ""}${relState ? `
|
|
7100
|
+
${relState}` : ""}
|
|
7101
|
+
|
|
7102
|
+
const mutation = useMutation({
|
|
7103
|
+
mutationFn: (data: Upsert${Singular}Input) => upsert${Singular}(data),
|
|
7104
|
+
onSuccess: () => {
|
|
7105
|
+
toast.success('${group3.title} saved')
|
|
7106
|
+
queryClient.invalidateQueries({ queryKey: ['${schema.name}'] })
|
|
7107
|
+
},
|
|
7108
|
+
onError: (error: Error) => {
|
|
7109
|
+
toast.error(error.message || 'Failed to save')
|
|
7110
|
+
}
|
|
7111
|
+
})
|
|
7112
|
+
|
|
7113
|
+
const isPending = mutation.isPending
|
|
7114
|
+
|
|
7115
|
+
const form = useForm<z.infer<typeof ${group3.varPrefix}Schema>>({
|
|
7116
|
+
resolver: zodResolver(${group3.varPrefix}Schema),
|
|
7117
|
+
defaultValues: {
|
|
7118
|
+
${defaultValues}
|
|
7119
|
+
}
|
|
7120
|
+
})
|
|
7121
|
+
${fieldArrayHooks ? `
|
|
7122
|
+
${fieldArrayHooks}
|
|
7123
|
+
` : ""}
|
|
7124
|
+
return (
|
|
7125
|
+
<Form {...form}>
|
|
7126
|
+
<form onSubmit={form.handleSubmit((values) => {
|
|
7127
|
+
const cleaned = Object.fromEntries(
|
|
7128
|
+
Object.entries(values).map(([key, value]) => [key, value === undefined ? '' : value])
|
|
7129
|
+
)
|
|
7130
|
+
mutation.mutate(cleaned as Upsert${Singular}Input)
|
|
7131
|
+
})}>
|
|
7132
|
+
<Card className="material-sm!">
|
|
7133
|
+
<CardHeader>
|
|
7134
|
+
<CardTitle>${group3.title}</CardTitle>${descriptionLine}
|
|
7135
|
+
</CardHeader>
|
|
7136
|
+
<CardContent className="space-y-6">
|
|
7137
|
+
${fieldsJSX}
|
|
7138
|
+
</CardContent>
|
|
7139
|
+
<CardFooter>
|
|
7140
|
+
<Button type="submit" disabled={isPending} size="sm">
|
|
7141
|
+
{isPending ? 'Saving...' : 'Save'}
|
|
7142
|
+
</Button>
|
|
7143
|
+
</CardFooter>
|
|
7144
|
+
</Card>
|
|
7145
|
+
</form>
|
|
7146
|
+
</Form>
|
|
7147
|
+
)
|
|
7148
|
+
}`;
|
|
7149
|
+
}
|
|
6972
7150
|
function generateSingleForm(schema, cwd, pagesDir, options = {}) {
|
|
6973
7151
|
const entityDir = path21.join(cwd, pagesDir, schema.name);
|
|
6974
7152
|
const formFilePath = path21.join(entityDir, `${schema.name}-form.tsx`);
|
|
@@ -6980,15 +7158,7 @@ function generateSingleForm(schema, cwd, pagesDir, options = {}) {
|
|
|
6980
7158
|
const allFormFields = schema.fields.filter(
|
|
6981
7159
|
(f) => !f.primaryKey && f.name !== "createdAt" && f.name !== "updatedAt"
|
|
6982
7160
|
);
|
|
6983
|
-
const
|
|
6984
|
-
const tabFieldNames = /* @__PURE__ */ new Set();
|
|
6985
|
-
for (const f of schema.fields) {
|
|
6986
|
-
if (f.type === "tabs" && f.tabs) {
|
|
6987
|
-
for (const tab of f.tabs) {
|
|
6988
|
-
if (tab.fields) for (const tf of tab.fields) tabFieldNames.add(tf.name);
|
|
6989
|
-
}
|
|
6990
|
-
}
|
|
6991
|
-
}
|
|
7161
|
+
const cardGroups = parseCardGroups(allFormFields, schema.label);
|
|
6992
7162
|
const hasBoolean = hasFieldType(schema.fields, "boolean");
|
|
6993
7163
|
const hasImage = hasFieldType(schema.fields, "image");
|
|
6994
7164
|
const hasVideo = hasFieldType(schema.fields, "video");
|
|
@@ -7003,48 +7173,20 @@ function generateSingleForm(schema, cwd, pagesDir, options = {}) {
|
|
|
7003
7173
|
const hasSeparator = hasFieldType(schema.fields, "separator");
|
|
7004
7174
|
const hasRelationship = hasFieldType(schema.fields, "relationship");
|
|
7005
7175
|
const hasTabsField = schema.fields.some((f) => f.type === "tabs");
|
|
7006
|
-
const
|
|
7007
|
-
const firstTabName = tabsField?.tabs?.[0]?.name || "";
|
|
7008
|
-
const mainRelFields = collectRelationshipFields(schema.fields, tabFieldNames);
|
|
7009
|
-
const listFieldsWithNested = [];
|
|
7010
|
-
function collectListFieldsSingle(fields) {
|
|
7011
|
-
for (const f of fields) {
|
|
7012
|
-
if (f.type === "list" && f.fields && f.fields.length > 0 && !f.hidden) {
|
|
7013
|
-
listFieldsWithNested.push(f);
|
|
7014
|
-
}
|
|
7015
|
-
if (f.type === "group" && f.fields) collectListFieldsSingle(f.fields);
|
|
7016
|
-
if (f.type === "tabs" && f.tabs) {
|
|
7017
|
-
for (const tab of f.tabs) {
|
|
7018
|
-
if (tab.fields) collectListFieldsSingle(tab.fields);
|
|
7019
|
-
}
|
|
7020
|
-
}
|
|
7021
|
-
}
|
|
7022
|
-
}
|
|
7023
|
-
collectListFieldsSingle(allFormFields);
|
|
7176
|
+
const flatFields = flattenFields(allFormFields);
|
|
7024
7177
|
const hasList = hasFieldType(schema.fields, "list");
|
|
7025
|
-
const hasNestedList = listFieldsWithNested.length > 0;
|
|
7178
|
+
const hasNestedList = cardGroups.some((g) => analyzeGroup(g.fields).listFieldsWithNested.length > 0);
|
|
7026
7179
|
const hasSimpleList = hasList && flatFields.some((f) => f.type === "list" && (!f.fields || f.fields.length === 0));
|
|
7027
|
-
const
|
|
7028
|
-
const
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
|
|
7033
|
-
|
|
7034
|
-
return ` <TabsContent value="${t.name}" className="space-y-6 p-6 rounded-2xl border bg-card">
|
|
7035
|
-
${tabFields}
|
|
7036
|
-
</TabsContent>`;
|
|
7037
|
-
}).join("\n");
|
|
7038
|
-
return ` <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
|
7039
|
-
<TabsList>
|
|
7040
|
-
${tabsList}
|
|
7041
|
-
</TabsList>
|
|
7042
|
-
${tabsContent}
|
|
7043
|
-
</Tabs>`;
|
|
7180
|
+
const allRelFields = [];
|
|
7181
|
+
for (const g of cardGroups) {
|
|
7182
|
+
const a = analyzeGroup(g.fields);
|
|
7183
|
+
for (const rf of a.relFields) {
|
|
7184
|
+
if (!allRelFields.some((e) => e.relationship === rf.relationship)) {
|
|
7185
|
+
allRelFields.push(rf);
|
|
7186
|
+
}
|
|
7044
7187
|
}
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
}).filter(Boolean).join("\n");
|
|
7188
|
+
}
|
|
7189
|
+
const needsReact = allRelFields.length > 0 || hasNestedList;
|
|
7048
7190
|
const uiImports = buildUiImports({
|
|
7049
7191
|
hasBoolean,
|
|
7050
7192
|
hasTextarea,
|
|
@@ -7063,30 +7205,29 @@ ${tabsContent}
|
|
|
7063
7205
|
hasSimpleList,
|
|
7064
7206
|
hasNestedList
|
|
7065
7207
|
});
|
|
7208
|
+
uiImports.push(`import {
|
|
7209
|
+
Card,
|
|
7210
|
+
CardContent,
|
|
7211
|
+
CardDescription,
|
|
7212
|
+
CardFooter,
|
|
7213
|
+
CardHeader,
|
|
7214
|
+
CardTitle
|
|
7215
|
+
} from '@cms/components/ui/card'`);
|
|
7066
7216
|
const lucideIcons = [];
|
|
7067
7217
|
if (hasRelationship) lucideIcons.push("Check", "ChevronsUpDown");
|
|
7068
7218
|
if (hasNestedList) {
|
|
7069
7219
|
if (!lucideIcons.includes("Plus")) lucideIcons.push("Plus");
|
|
7070
7220
|
if (!lucideIcons.includes("X")) lucideIcons.push("X");
|
|
7071
7221
|
}
|
|
7072
|
-
const relHookImports =
|
|
7222
|
+
const relHookImports = allRelFields.map((f) => {
|
|
7073
7223
|
const relPlural = toPascalCase(pluralize(f.relationship || ""));
|
|
7074
7224
|
return `import { use${relPlural} } from '@cms/hooks/use-${f.relationship}'`;
|
|
7075
7225
|
}).filter((v, i, a) => a.indexOf(v) === i).join("\n");
|
|
7076
|
-
const
|
|
7077
|
-
const
|
|
7078
|
-
return
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
const fieldArrayHooks = listFieldsWithNested.map((field) => {
|
|
7082
|
-
const pascalFieldName = toPascalCase(field.name);
|
|
7083
|
-
return ` const [${field.name}Expanded, set${pascalFieldName}Expanded] = React.useState<string | undefined>(undefined)
|
|
7084
|
-
const ${field.name}FieldArray = useFieldArray({
|
|
7085
|
-
control: form.control,
|
|
7086
|
-
name: '${field.name}'
|
|
7087
|
-
})`;
|
|
7088
|
-
}).join("\n");
|
|
7089
|
-
const needsReact = mainRelFields.length > 0 || hasNestedList;
|
|
7226
|
+
const cardComponents = cardGroups.map((group3) => {
|
|
7227
|
+
const analysis = analyzeGroup(group3.fields);
|
|
7228
|
+
return generateCardComponent(group3, schema, analysis);
|
|
7229
|
+
});
|
|
7230
|
+
const cardRenders = cardGroups.map((g) => ` <${g.componentName} initialData={initialData} />`).join("\n");
|
|
7090
7231
|
const content = `'use client'
|
|
7091
7232
|
${needsReact ? "\nimport * as React from 'react'" : ""}
|
|
7092
7233
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|
@@ -7103,75 +7244,17 @@ import type {
|
|
|
7103
7244
|
} from '@cms/actions/${schema.name}'
|
|
7104
7245
|
import { upsert${Singular} } from '@cms/actions/${schema.name}'
|
|
7105
7246
|
|
|
7106
|
-
|
|
7107
|
-
${zodFields}
|
|
7108
|
-
})
|
|
7109
|
-
|
|
7110
|
-
export type FormValues = z.infer<typeof formSchema>
|
|
7247
|
+
${cardComponents.join("\n\n")}
|
|
7111
7248
|
|
|
7112
7249
|
interface ${Singular}FormProps {
|
|
7113
7250
|
initialData?: ${Singular}Data | null
|
|
7114
7251
|
}
|
|
7115
7252
|
|
|
7116
7253
|
export function ${Singular}Form({ initialData }: ${Singular}FormProps) {
|
|
7117
|
-
const queryClient = useQueryClient()${hasTabsField ? `
|
|
7118
|
-
const [activeTab, setActiveTab] = useQueryState('tab', { defaultValue: '${firstTabName}' })` : ""}${relState ? `
|
|
7119
|
-
${relState}` : ""}
|
|
7120
|
-
|
|
7121
|
-
const upsertMutation = useMutation({
|
|
7122
|
-
mutationFn: (data: Upsert${Singular}Input) => upsert${Singular}(data),
|
|
7123
|
-
onSuccess: () => {
|
|
7124
|
-
toast.success('${schema.label} saved successfully')
|
|
7125
|
-
queryClient.invalidateQueries({ queryKey: ['${schema.name}'] })
|
|
7126
|
-
},
|
|
7127
|
-
onError: (error: Error) => {
|
|
7128
|
-
toast.error(error.message || 'Failed to save ${schema.label.toLowerCase()}')
|
|
7129
|
-
}
|
|
7130
|
-
})
|
|
7131
|
-
|
|
7132
|
-
const isPending = upsertMutation.isPending
|
|
7133
|
-
|
|
7134
|
-
const form = useForm<FormValues>({
|
|
7135
|
-
resolver: zodResolver(formSchema),
|
|
7136
|
-
defaultValues: {
|
|
7137
|
-
${defaultValues}
|
|
7138
|
-
}
|
|
7139
|
-
})
|
|
7140
|
-
${fieldArrayHooks ? `
|
|
7141
|
-
${fieldArrayHooks}
|
|
7142
|
-
` : ""}
|
|
7143
|
-
function onSubmit(values: FormValues) {
|
|
7144
|
-
const cleanedValues = Object.fromEntries(
|
|
7145
|
-
Object.entries(values).map(([key, value]) => [key, value === undefined ? '' : value])
|
|
7146
|
-
) as FormValues
|
|
7147
|
-
|
|
7148
|
-
upsertMutation.mutate(cleanedValues as Upsert${Singular}Input)
|
|
7149
|
-
}
|
|
7150
|
-
|
|
7151
7254
|
return (
|
|
7152
|
-
<
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
const firstError = Object.values(errors)[0]
|
|
7156
|
-
if (firstError?.message) {
|
|
7157
|
-
toast.error(String(firstError.message))
|
|
7158
|
-
} else {
|
|
7159
|
-
toast.error('Please fix the form errors before submitting')
|
|
7160
|
-
}
|
|
7161
|
-
})} className="space-y-6">
|
|
7162
|
-
<div className="space-y-6 p-6 rounded-2xl border bg-card">
|
|
7163
|
-
${formFieldsJSX}
|
|
7164
|
-
</div>
|
|
7165
|
-
|
|
7166
|
-
<div className="flex items-center fixed bottom-0 md:left-[calc(var(--sidebar-width))] w-screen md:w-[calc(100svw-var(--sidebar-width)-4px)] right-0 bg-secondary border-t">
|
|
7167
|
-
<div className="flex mx-auto py-4 w-full max-w-5xl items-center justify-end gap-2">
|
|
7168
|
-
<Button type="submit" disabled={isPending} size="lg">
|
|
7169
|
-
{isPending ? 'Saving...' : 'Save'}
|
|
7170
|
-
</Button>
|
|
7171
|
-
</div>
|
|
7172
|
-
</div>
|
|
7173
|
-
</form>
|
|
7174
|
-
</Form>
|
|
7255
|
+
<div className="space-y-6">
|
|
7256
|
+
${cardRenders}
|
|
7257
|
+
</div>
|
|
7175
7258
|
)
|
|
7176
7259
|
}
|
|
7177
7260
|
`;
|
|
@@ -9998,14 +10081,28 @@ function defaultSettingsSchema() {
|
|
|
9998
10081
|
name: "settings",
|
|
9999
10082
|
type: "single",
|
|
10000
10083
|
label: "Settings",
|
|
10001
|
-
description: "General
|
|
10084
|
+
description: "General Settings",
|
|
10002
10085
|
icon: "Settings",
|
|
10003
10086
|
fields: [
|
|
10004
|
-
{
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
|
|
10087
|
+
{
|
|
10088
|
+
type: "section",
|
|
10089
|
+
name: "siteSettings",
|
|
10090
|
+
label: "Site Settings",
|
|
10091
|
+
description: "General settings for the site",
|
|
10092
|
+
fields: [
|
|
10093
|
+
{ name: "appName", type: "string", label: "App Name", hint: "Displayed in the sidebar and throughout the dashboard", default: "BetterStart" },
|
|
10094
|
+
{ name: "appDescription", type: "text", label: "App Description", hint: "A brief description of the application" }
|
|
10095
|
+
]
|
|
10096
|
+
},
|
|
10097
|
+
{
|
|
10098
|
+
type: "section",
|
|
10099
|
+
name: "branding",
|
|
10100
|
+
label: "Branding",
|
|
10101
|
+
description: "Logo and visual identity",
|
|
10102
|
+
fields: [
|
|
10103
|
+
{ name: "logo", type: "image", label: "Logo" }
|
|
10104
|
+
]
|
|
10105
|
+
}
|
|
10009
10106
|
]
|
|
10010
10107
|
},
|
|
10011
10108
|
null,
|