@postxl/generator 0.56.7 → 0.57.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/dist/generator.js CHANGED
@@ -67,6 +67,7 @@ const selectors_generator_1 = require("./generators/indices/selectors.generator"
67
67
  const stubs_generator_1 = require("./generators/indices/stubs.generator");
68
68
  const testdata_service_generator_1 = require("./generators/indices/testdata-service.generator");
69
69
  const types_generator_2 = require("./generators/indices/types.generator");
70
+ const admin_page_generator_1 = require("./generators/models/admin.page.generator");
70
71
  const businesslogic_update_generator_1 = require("./generators/models/businesslogic-update.generator");
71
72
  const businesslogic_view_generator_1 = require("./generators/models/businesslogic-view.generator");
72
73
  const importexport_decoder_generator_1 = require("./generators/models/importexport-decoder.generator");
@@ -98,6 +99,7 @@ const CONFIG_SCHEMA = zod_1.z
98
99
  trpcRoutesFolder: zod_1.z.string().optional(),
99
100
  reactFolderOutput: zod_1.z.string().optional(),
100
101
  prismaMigrationsFolder: zod_1.z.string().optional(),
102
+ pathToAdminPages: zod_1.z.string().optional(),
101
103
  randomSeed: zod_1.z
102
104
  .string()
103
105
  .optional()
@@ -123,6 +125,7 @@ const CONFIG_SCHEMA = zod_1.z
123
125
  seedDataPath: (0, types_1.toPath)(s.pathToSeedData || './backend/seed-data/src/'),
124
126
  seedLibPath: (0, types_1.toPath)(s.pathToSeedLib || './backend/libs/seed/src/'),
125
127
  trpcRoutesFolderPath: (0, types_1.toPath)(s.trpcRoutesFolder || './backend/libs/trpc/src/routes/'),
128
+ adminPagesFolderPath: (0, types_1.toPath)(s.pathToAdminPages || './web/src/pages/admin/'),
126
129
  },
127
130
  randomSeed: s.randomSeed,
128
131
  force: s.force,
@@ -191,6 +194,8 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
191
194
  generated.write(`/${meta.trpc.routerFilePath}.ts`, (0, route_generator_1.generateRoute)({ model, meta }));
192
195
  // React
193
196
  yield generated.copy((0, react_generator_2.generateReactComponentsForModel)({ model, meta }), meta.react.folderPath);
197
+ // Admin
198
+ generated.write(`/${meta.admin.filePath}.tsx`, (0, admin_page_generator_1.generateAdminPage)({ meta }));
194
199
  logger.log(`- ${model.name} processed`);
195
200
  }
196
201
  // Generate Enums
@@ -0,0 +1,7 @@
1
+ import { ModelMetaData } from '../../lib/meta';
2
+ /**
3
+ * returns an admin page for a given model.
4
+ */
5
+ export declare function generateAdminPage({ meta }: {
6
+ meta: ModelMetaData;
7
+ }): string;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateAdminPage = void 0;
4
+ const imports_1 = require("../../lib/imports");
5
+ /**
6
+ * returns an admin page for a given model.
7
+ */
8
+ function generateAdminPage({ meta }) {
9
+ const { react } = meta;
10
+ const imports = imports_1.ImportsGenerator.from(meta.admin.filePath).addImport({
11
+ items: [react.components.libraryComponentName, react.components.modals.createComponentName],
12
+ from: meta.react.importPath,
13
+ });
14
+ return `
15
+ import styled from 'styled-components'
16
+
17
+ import { ActionWrapper, ActionsBarWrapper, Search, Spacer } from '@components/atoms/ActionsBar'
18
+ import { Button } from '@components/atoms/Button'
19
+ import { Headline } from '@components/atoms/Headline'
20
+
21
+ ${imports.generate()}
22
+
23
+ import { Content, Layout } from '@components/shared/Layout'
24
+ import { t } from '@i18n/translation'
25
+ import { useState } from 'react'
26
+
27
+ export default function Admin${meta.internalSingularNameCapitalized}Page() {
28
+ const [query, setQuery] = useState('')
29
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false)
30
+
31
+ return (
32
+ <Layout>
33
+ <Header>
34
+ <Headline label="${meta.userFriendlyName}" />
35
+ <ActionsBarWrapper>
36
+ <Search
37
+ key="Search"
38
+ placeholder="Search ${meta.userFriendlyNamePlural}"
39
+ icon="magnifying-glass"
40
+ value={query}
41
+ onChange={(e) => setQuery(e.target.value)}
42
+ />
43
+
44
+ <Spacer key="Spacer" />
45
+
46
+ <ActionWrapper key="GlobalFilter">
47
+ <Button label={t['Create']} icon="plus" fill="fill" __cypress_selector__="indexPage-buttons-create" onClick={() => setIsCreateModalOpen(true)}/>
48
+ </ActionWrapper>
49
+ </ActionsBarWrapper>
50
+ </Header>
51
+
52
+ <Content>
53
+ <${meta.react.components.libraryComponentName} />
54
+ </Content>
55
+
56
+ <${meta.react.components.modals.createComponentName}
57
+ show={isCreateModalOpen}
58
+ onHide={() => setIsCreateModalOpen(false)}
59
+ />
60
+ </Layout>
61
+ )
62
+ }
63
+
64
+ const Header = styled.header\`
65
+ display: flex;
66
+ flex-direction: column;
67
+ align-items: stretch;
68
+
69
+ gap: var(--headline-spacing);
70
+ \`
71
+ `;
72
+ }
73
+ exports.generateAdminPage = generateAdminPage;
@@ -188,7 +188,7 @@ function generateModelBusinessLogicUpdate({ model, meta }) {
188
188
  */
189
189
  export const ${zodCloneObject} = z.object({
190
190
  ${model.fields
191
- .filter((f) => !f.attributes.isReadonly && f.kind !== "relation" || f.kind === 'id')
191
+ .filter((f) => !f.attributes.isReadonly || f.kind === "id")
192
192
  .map((f) => `${f.name}: z.${(0, zod_1.getZodDecoderDefinition)({ field: f, allowAnyOptionalField: f.kind !== 'id' })}`)
193
193
  .join(',')}
194
194
  })
@@ -333,11 +333,11 @@ function generateCloneMethod({ model, meta, m }) {
333
333
  const refModelMeta = (0, meta_1.getModelMetadata)({ model: referencingModel });
334
334
  const refFieldMeta = (0, meta_1.getFieldMetadata)({ field: referencingField });
335
335
  backReferenceNames.push(`${refModelMeta.userFriendlyNamePlural}.${referencingField.name}`);
336
+ const { view, update } = refModelMeta.businessLogic;
336
337
  backReferenceCloning.push(`
337
338
  // ${referencingModel.name}.${referencingField.name}
338
- for (const childId of await this.viewService.${refModelMeta.businessLogic.view.serviceVariableName}.data.${refFieldMeta.getByForeignKeyIdsMethodFnName}(id)) {
339
- const clonedChild = await this.updateService.${refModelMeta.businessLogic.update.serviceVariableName}.clone({ data: { id: childId }, execution})
340
- await this.updateService.${refModelMeta.businessLogic.update.serviceVariableName}.update({ data: { id: clonedChild.id, ${referencingField.name}: clone.id }, execution })
339
+ for (const childId of await this.viewService.${view.serviceVariableName}.data.${refFieldMeta.getByForeignKeyIdsMethodFnName}(id)) {
340
+ await this.updateService.${update.serviceVariableName}.clone({ data: { id: childId, ${referencingField.name}: clone.id }, execution })
341
341
  }
342
342
  `);
343
343
  }
@@ -351,7 +351,7 @@ function generateCloneMethod({ model, meta, m }) {
351
351
  throw new Error(\`${meta.userFriendlyName} with id \${id} not found\`)
352
352
  }
353
353
 
354
- const clone = await this.create({ data: { ...omitId(source), ...data }, execution })
354
+ const clone = await this.data.create({ item: { ...omitId(source), ...data }, execution })
355
355
 
356
356
  ${backReferenceCloning.join('\n')}
357
357
 
@@ -993,6 +993,10 @@ export type ModelMetaData = {
993
993
  * The path to the folder that contains React components for this model.
994
994
  */
995
995
  folderPath: Types.FilePath;
996
+ /**
997
+ * Globally accessible import path for the model's React components.
998
+ */
999
+ importPath: Types.FilePath;
996
1000
  context: {
997
1001
  /**
998
1002
  * Name of the function that should be used as React hook (e.g. `useAggregationContext`).
@@ -1087,6 +1091,15 @@ export type ModelMetaData = {
1087
1091
  reactQueryMethod: Types.FunctionName;
1088
1092
  };
1089
1093
  };
1094
+ /**
1095
+ * Properties provided by the `admin.page` generators.
1096
+ */
1097
+ admin: {
1098
+ /**
1099
+ * The absolute file path of the admin page for this model.
1100
+ */
1101
+ filePath: Types.FilePath;
1102
+ };
1090
1103
  /**
1091
1104
  * Properties provided by the `types` generators.
1092
1105
  */
package/dist/lib/meta.js CHANGED
@@ -358,6 +358,7 @@ function getModelMetadata({ model }) {
358
358
  react: {
359
359
  folderName: Types.toFolderName(`${PascalCase}`),
360
360
  folderPath: Types.toPath(`${config.paths.reactFolderPath}models/${PascalCase}/`),
361
+ importPath: Types.toPath(`@components/models/${PascalCase}`),
361
362
  context: {
362
363
  hookFnName: Types.toFunctionName(`use${PascalCase}Context`),
363
364
  instanceGetterHookFnName: Types.toFunctionName(`use${PascalCase}`),
@@ -402,6 +403,9 @@ function getModelMetadata({ model }) {
402
403
  reactQueryMethod: Types.toFunctionName(`${uncapitalizedPlural}.delete`),
403
404
  },
404
405
  },
406
+ admin: {
407
+ filePath: Types.toPath(`${config.paths.adminPagesFolderPath}/${uncapitalized}`),
408
+ },
405
409
  types: {
406
410
  importPath: Types.toBackendModulePath(`@backend/types`),
407
411
  filePath: Types.toPath(`${config.paths.modelTypeDefinitionsPath}${camelCase}.type`),
@@ -87,6 +87,10 @@ export type SchemaConfig = {
87
87
  * Path to the directory containing trpc routes.
88
88
  */
89
89
  trpcRoutesFolderPath: Types.FilePath;
90
+ /**
91
+ * Path to the directory containing admin pages.
92
+ */
93
+ adminPagesFolderPath: Types.FilePath;
90
94
  };
91
95
  /**
92
96
  * Whether the generator should overwrite existing files.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postxl/generator",
3
- "version": "0.56.7",
3
+ "version": "0.57.0",
4
4
  "main": "./dist/generator.js",
5
5
  "typings": "./dist/generator.d.ts",
6
6
  "bin": {