@simitgroup/simpleapp-generator 2.0.3-b-alpha → 2.0.3-d-alpha

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.
Files changed (50) hide show
  1. package/.claude/settings.local.json +10 -0
  2. package/ReleaseNote.md +9 -0
  3. package/dist/buildinschemas/autoincreament.d.ts +3 -0
  4. package/dist/buildinschemas/autoincreament.d.ts.map +1 -0
  5. package/dist/buildinschemas/autoincreament.js +39 -0
  6. package/dist/buildinschemas/autoincreament.js.map +1 -0
  7. package/dist/buildinschemas/docnoformat.d.ts +3 -0
  8. package/dist/buildinschemas/docnoformat.d.ts.map +1 -0
  9. package/dist/buildinschemas/docnoformat.js +58 -0
  10. package/dist/buildinschemas/docnoformat.js.map +1 -0
  11. package/dist/buildinschemas/documentevent.d.ts +3 -0
  12. package/dist/buildinschemas/documentevent.d.ts.map +1 -0
  13. package/dist/buildinschemas/documentevent.js +48 -0
  14. package/dist/buildinschemas/documentevent.js.map +1 -0
  15. package/dist/buildinschemas/webhooklog.d.ts +3 -0
  16. package/dist/buildinschemas/webhooklog.d.ts.map +1 -0
  17. package/dist/buildinschemas/webhooklog.js +79 -0
  18. package/dist/buildinschemas/webhooklog.js.map +1 -0
  19. package/dist/framework.d.ts.map +1 -1
  20. package/dist/framework.js +8 -18
  21. package/dist/framework.js.map +1 -1
  22. package/dist/generate.d.ts.map +1 -1
  23. package/dist/generate.js +35 -19
  24. package/dist/generate.js.map +1 -1
  25. package/dist/index.js +7 -17
  26. package/dist/index.js.map +1 -1
  27. package/dist/libs.d.ts.map +1 -1
  28. package/dist/processors/bpmnbuilder.d.ts.map +1 -1
  29. package/dist/processors/bpmnbuilder.js +7 -17
  30. package/dist/processors/bpmnbuilder.js.map +1 -1
  31. package/dist/processors/jrxmlbuilder.d.ts.map +1 -1
  32. package/dist/processors/jrxmlbuilder.js +7 -17
  33. package/dist/processors/jrxmlbuilder.js.map +1 -1
  34. package/dist/processors/jsonschemabuilder.d.ts.map +1 -1
  35. package/dist/processors/jsonschemabuilder.js +2 -0
  36. package/dist/processors/jsonschemabuilder.js.map +1 -1
  37. package/dist/type.d.ts +1 -0
  38. package/dist/type.d.ts.map +1 -1
  39. package/dist/type.js.map +1 -1
  40. package/package.json +3 -3
  41. package/src/generate.ts +27 -1
  42. package/src/processors/jsonschemabuilder.ts +1 -0
  43. package/src/type.ts +1 -0
  44. package/templates/basic/nest/default.ts.eta +61 -2
  45. package/templates/basic/nest/enum.ts.eta +86 -12
  46. package/templates/basic/nest/schema.ts.eta +58 -7
  47. package/templates/nest/src/simple-app/_core/features/profile/profile.service.ts.eta +15 -18
  48. package/templates/nest/src/simple-app/_core/framework/simple-app.interceptor.ts.eta +2 -1
  49. package/templates/nest/src/simple-app/_core/utils/string-utils.ts.eta +26 -3
  50. package/templates/nuxt/composables/getDocument.generate.ts.eta +48 -37
@@ -12,17 +12,17 @@ import * as mongoose from 'mongoose';
12
12
 
13
13
  export * from 'src/simple-app/_core/framework/schemas';
14
14
 
15
- <%
15
+ <%
16
16
  const getFieldType =(proptype, typename,actionname)=>{
17
17
  const systemtypes = ['Boolean','boolean','string','String','number','Number','object','Object','integer','Integer']
18
18
  if(!typename) throw actionname +" undefine schema";
19
- let tmptypename = typename.replace('[','').replace(']','')
19
+ let tmptypename = typename.replace('[','').replace(']','')
20
20
  if(proptype=='schema'){
21
- tmptypename= upperFirstCase(tmptypename)
21
+ tmptypename= upperFirstCase(tmptypename)
22
22
  }else if(systemtypes.includes(typename)){
23
23
  tmptypename = tmptypename.toLowerCase();
24
24
  }
25
-
25
+
26
26
  if(typename.includes('[')){
27
27
  return proptype=='schema' ? `[${tmptypename}]` : `${tmptypename}[]`
28
28
  }else{
@@ -31,6 +31,42 @@ export * from 'src/simple-app/_core/framework/schemas';
31
31
  }
32
32
  %>
33
33
 
34
+ <%
35
+ /**
36
+ * ENUM NAME RESOLUTION
37
+ *
38
+ * buildDefEnumMap and resolveEnumBaseName are shared helpers injected by getCodeGenHelper()
39
+ * in generate.ts — the single source of truth for this logic.
40
+ *
41
+ * buildDefEnumMap: reads $defs from the schema, returns Map<fingerprint, baseName>
42
+ * e.g. licenseType in $defs + resourceName "tenant" → "TenantLicenseType"
43
+ *
44
+ * resolveEnumBaseName: looks up fingerprint in map (shared $defs enum),
45
+ * falls back to modelName+fieldName for inline enums
46
+ * e.g. model "TenantSubscription", field "currency" → "TenantSubscriptionCurrency"
47
+ */
48
+ const defEnumMap = buildDefEnumMap(it.jsonschema);
49
+
50
+ // Pre-pass: walk all models to collect every enum class name that will be used.
51
+ // This lets us generate a single import statement at the top of the file,
52
+ // so @Prop and @ApiProperty can reference the enum class directly instead of passing inline values.
53
+ const usedEnumNames = new Set();
54
+ for (const [modelName, modelData] of Object.entries(it.models)) {
55
+ for (const [fieldName, fieldObj] of Object.entries(modelData.model)) {
56
+ if (fieldObj && typeof fieldObj === 'object' && !Array.isArray(fieldObj) &&
57
+ fieldObj.type === 'string' && Array.isArray(fieldObj.enum) && fieldObj.enum.length > 0) {
58
+ // toTypeName converts raw model key (e.g. "TuitionclassScheduleSetting") to correctly-cased
59
+ // typename (e.g. "TuitionClassScheduleSetting") — same convention enum.ts.eta uses
60
+ usedEnumNames.add(resolveEnumBaseName(fieldObj.enum, defEnumMap, toTypeName(it.resourceName, modelName), fieldName) + 'Enum');
61
+ }
62
+ }
63
+ }
64
+ %>
65
+
66
+ <% if (usedEnumNames.size > 0) { %>
67
+ import { <%~ [...usedEnumNames].join(', ') %> } from './<%= camelToKebab(it.resourceName) %>.enum';
68
+ <% } %>
69
+
34
70
  <% const draw = (it, obj, key, currentModelName) => { %>
35
71
  <%
36
72
  let required = false;
@@ -67,11 +103,24 @@ export * from 'src/simple-app/_core/framework/schemas';
67
103
  @Field(() => [<%=newtypename %>])
68
104
  @ApiProperty({type: ()=>[<%= newtypename %>], required: <%= required %> })
69
105
  <%= key %> <%=required ? '' :'?' %>: <%= getFieldType('type', newtypename, '') %>[];
106
+ <% } else if(obj.type === 'string' && Array.isArray(obj.enum) && obj.enum.length > 0) { %>
107
+ <%
108
+ // Resolve the enum class name, then pass values inline to avoid needing an import.
109
+ // @Prop — tells Mongoose to validate against allowed values
110
+ // @Field — GraphQL treats it as String (enums need a separate GQL enum registration)
111
+ // @ApiProperty — enum + enumName tells Swagger to render a dropdown with the correct type name
112
+ const enumBaseName = resolveEnumBaseName(obj.enum, defEnumMap, toTypeName(it.resourceName, currentModelName), key);
113
+ const enumClassName = enumBaseName + 'Enum';
114
+ %>
115
+ @Prop()
116
+ @Field(() => String)
117
+ @ApiProperty({ enum: <%= enumClassName %>, required: <%= required %> })
118
+ <%= key %> <%=required ? '' :'?' %>: <%= enumClassName %>;
70
119
  <% }else{%>
71
120
  @Prop()
72
- @Field()
121
+ @Field()
73
122
  @ApiProperty({type: () => <%= getFieldType('schema', obj.type,'') %> , required: <%= required %>})
74
- <%= key %> <%=required ? '' :'?' %>: <%= getFieldType('type', obj.type,'') %>;
123
+ <%= key %> <%=required ? '' :'?' %>: <%= getFieldType('type', obj.type,'') %>;
75
124
  <% } %>
76
125
  <% } %>
77
126
 
@@ -123,7 +172,9 @@ export class <%=autoCompleteModelName %> {
123
172
 
124
173
  <% if(it.moreAutoComplete && Array.isArray(it.moreAutoComplete)) { %>
125
174
  <% it.moreAutoComplete.forEach(function(field){ %>
126
- <% draw(it, it.models[it.apiSchemaName].model[field], field, autoCompleteModelName) %>
175
+ <% /* Pass it.apiSchemaName (main model) not autoCompleteModelName so enum names resolve
176
+ to the same class as the main schema (e.g. CategoryCategoryTypeEnum, not CategoryAutoCompleteCategoryTypeEnum) */ %>
177
+ <% draw(it, it.models[it.apiSchemaName].model[field], field, it.apiSchemaName) %>
127
178
 
128
179
  <% }) %>
129
180
  <% } %>
@@ -4,27 +4,25 @@
4
4
  * Author: Ks Tan
5
5
  */
6
6
 
7
+ import { BadRequestException, Injectable, Logger } from '@nestjs/common';
7
8
  import countrytimezone from 'countries-and-timezones';
8
- import countryToCurrency, { Currencies, Countries } from 'country-to-currency';
9
- import { Injectable, Scope, Inject, Logger, BadRequestException, InternalServerErrorException } from '@nestjs/common';
9
+ import countryToCurrency from 'country-to-currency';
10
10
  import { UserContext } from '../user-context/user.context';
11
11
  // import { RegTenant } from './profile.types';
12
- import * as mongoose from 'mongoose';
13
- import { InjectConnection } from '@nestjs/mongoose';
14
- import { TenantService } from '../../resources/tenant/tenant.service';
15
- import { OrganizationService } from '../../resources/organization/organization.service';
16
12
  import { BranchService } from '../../resources/branch/branch.service';
17
- import { UserService } from '../../resources/user/user.service';
13
+ import { OrganizationService } from '../../resources/organization/organization.service';
18
14
  import { PermissionService } from '../../resources/permission/permission.service';
15
+ import { TenantService } from '../../resources/tenant/tenant.service';
16
+ import { UserService } from '../../resources/user/user.service';
19
17
 
20
- import { Tenant } from '../../resources/tenant/tenant.schema';
21
- import { Organization } from '../../resources/organization/organization.schema';
22
18
  import { Branch } from '../../resources/branch/branch.schema';
23
- import { User } from '../../resources/user/user.schema';
19
+ import { Organization } from '../../resources/organization/organization.schema';
24
20
  import { KeyValue, Permission } from '../../resources/permission/permission.schema';
21
+ import { Tenant } from '../../resources/tenant/tenant.schema';
22
+ import { User } from '../../resources/user/user.schema';
25
23
 
26
- import { UserContextInfo } from './profile.schema';
27
24
  import { PhotoService } from 'src/simple-app/features/upload-file/photo/photo.service';
25
+ import { UserContextInfo } from './profile.schema';
28
26
  const Base64URL = require('@darkwolf/base64url');
29
27
  @Injectable()
30
28
  export class ProfileService {
@@ -90,13 +88,12 @@ export class ProfileService {
90
88
  async createTenant(appuser: UserContext, tenantName: string, timeZone: string, utcOffset: number, businessType: string, mobileNo: string, interestedInSales?: boolean) {
91
89
  // try{
92
90
 
93
-
94
91
  const timezonedata = countrytimezone.getCountriesForTimezone(timeZone)[0];
95
92
  const countryCode = timezonedata['id'];
96
93
  const countryName = timezonedata['name'];
97
94
  const currencyCode = countryToCurrency[countryCode];
98
95
 
99
- appuser.startTransaction()
96
+ appuser.startTransaction();
100
97
  const tenantdata: Tenant = {
101
98
  tenantId: 1,
102
99
  tenantName: tenantName,
@@ -159,13 +156,13 @@ export class ProfileService {
159
156
  workEnd: '22:00:00',
160
157
  workingHours: [],
161
158
  offdays: ['sat', 'sun'],
162
- street1: '11, My Street 1',
163
- street2: 'My Street 2',
164
- postcode: '11111',
165
- city: 'My City',
159
+ street1: '',
160
+ street2: '',
161
+ postcode: '',
162
+ city: '',
166
163
  tel: mobileNo,
167
164
  email: appuser.getEmail(),
168
- region: 'My Region',
165
+ region: '',
169
166
  country: countryName,
170
167
  active: true,
171
168
  orgId: orgResult.orgId,
@@ -11,6 +11,7 @@ import type { Response as ExpressResponse } from 'express';
11
11
  import { Connection, Model } from 'mongoose';
12
12
  import { catchError, map, tap } from 'rxjs/operators';
13
13
  import { UserContext } from '../features/user-context/user.context';
14
+ import { getErrorMessage } from '../utils/error';
14
15
  import { SimpleAppDbRevertService } from './simple-app-db-revert.service';
15
16
  @Injectable()
16
17
  export class SimpleAppInterceptor implements NestInterceptor {
@@ -92,7 +93,7 @@ export class SimpleAppInterceptor implements NestInterceptor {
92
93
 
93
94
  const responseBody = {
94
95
  // eslint-disable-next-line
95
- message: err.message,
96
+ message: getErrorMessage(err).message ?? err.message,
96
97
  timestamp: new Date().toISOString(),
97
98
  path: url,
98
99
  // eslint-disable-next-line
@@ -1,4 +1,27 @@
1
- export const camelToKebab = (value) => value.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
1
+ export const camelToKebab = (value: string) => value.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
2
2
 
3
- export const escapeRegExp = (value: string): string =>
4
- value.replace(/[\\^$.*+?()\[\]{}|]/g, '\\$&');
3
+ export const isValidEmail = (value: unknown): value is string => typeof value === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
4
+
5
+ export const nameFromEmail = (email: string): string => {
6
+ return email.split('@')[0];
7
+ };
8
+
9
+ export const escapeRegExp = (value: string): string => value.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
10
+
11
+ export function paddedUniqueCodeGenerator(usedCodes: Set<string>, prefix = 'LVL', padSize = 3) {
12
+ let index = 1;
13
+
14
+ const format = (n: number) => `${prefix}_${String(n).padStart(padSize, '0')}`;
15
+
16
+ return () => {
17
+ let code: string;
18
+
19
+ do {
20
+ code = format(index++);
21
+ } while (usedCodes.has(code));
22
+
23
+ usedCodes.add(code);
24
+
25
+ return code;
26
+ };
27
+ }
@@ -4,42 +4,53 @@
4
4
  * last change 2024-02-24
5
5
  * Author: Ks Tan
6
6
  */
7
- import {getAllDocuments} from '~/simpleapp/generate/commons/documents'
8
- import { FormCrudEvent,SimpleAppDocumentType } from '~/types'
9
- export const getDocument = (docname:SimpleAppDocumentType) =>{
10
- return getAllDocuments().find((item)=>item.docName==docname) //?.docClass
11
- }
7
+ import { getAllDocuments } from "~/simpleapp/generate/commons/documents";
8
+ import { FormCrudEvent, SimpleAppDocumentType } from "~/types";
9
+ export const getDocument = (docname: SimpleAppDocumentType | string) => {
10
+ const normalizedDocName = `${docname ?? ""}`.trim().toLowerCase();
11
+ if (!normalizedDocName) return undefined;
12
+
13
+ return getAllDocuments().find((item) => item.docName?.toLowerCase() == normalizedDocName); //?.docClass
14
+ };
12
15
 
13
16
  // (appuser: UserContext, filter: any) => Promise<void>;
14
- export const onScreenAddDocument = <T>(docname:SimpleAppDocumentType,paras?:T,after?:(eventType:FormCrudEvent, data:T,visible:Ref<boolean>)=>Promise<void>)=>{
15
- useNuxtApp().$event('ViewRecord',{
16
- documentName: docname,
17
- eventId:randomUUID(),
18
- _id:'new',
19
- paras:paras,
20
- document: getDocument(docname)?.docClass,
21
- viewer: getDocument(docname)?.viewer,
22
- after: (eventType:FormCrudEvent, data: any,visible:Ref<boolean>) => {
23
- //do nothing
24
- if(after)after(eventType,data as T,visible)
25
- },
26
- label:t(docname)
27
-
28
- })
29
- }
30
- export const onScreenEditDocument = <T>(docname:SimpleAppDocumentType,_id:string,after?:(eventType:FormCrudEvent, data:T,visible:Ref<boolean>)=>Promise<void>,autoclose:number=1)=>{
31
-
32
- useNuxtApp().$event('ViewRecord',{
33
- documentName: docname,
34
- eventId:randomUUID(),
35
- _id:_id,
36
- autoclose:autoclose,
37
- document: getDocument(docname)?.docClass,
38
- viewer: getDocument(docname)?.viewer,
39
- after: async (eventType:FormCrudEvent, data: any,visible:Ref<boolean>) => {
40
- //do nothing
41
- if(after)after(eventType,data as T,visible)
42
- },
43
- label:t(docname)
44
- })
45
- }
17
+ export const onScreenAddDocument = <T>(
18
+ docname: SimpleAppDocumentType,
19
+ paras?: T,
20
+ after?: (eventType: FormCrudEvent, data: T, visible: Ref<boolean>) => Promise<void>,
21
+ ) => {
22
+ useNuxtApp().$event("ViewRecord", {
23
+ documentName: docname,
24
+ eventId: randomUUID(),
25
+ _id: "new",
26
+ paras: paras,
27
+ document: getDocument(docname)?.docClass,
28
+ viewer: getDocument(docname)?.viewer,
29
+ after: (eventType: FormCrudEvent, data: any, visible: Ref<boolean>) => {
30
+ //do nothing
31
+ if (after) after(eventType, data as T, visible);
32
+ },
33
+ label: t(docname),
34
+ });
35
+ };
36
+
37
+ export const onScreenEditDocument = <T>(
38
+ docname: SimpleAppDocumentType,
39
+ _id: string,
40
+ after?: (eventType: FormCrudEvent, data: T, visible: Ref<boolean>) => Promise<void>,
41
+ autoclose: number = 1,
42
+ ) => {
43
+ useNuxtApp().$event("ViewRecord", {
44
+ documentName: docname,
45
+ eventId: randomUUID(),
46
+ _id: _id,
47
+ autoclose: autoclose,
48
+ document: getDocument(docname)?.docClass,
49
+ viewer: getDocument(docname)?.viewer,
50
+ after: async (eventType: FormCrudEvent, data: any, visible: Ref<boolean>) => {
51
+ //do nothing
52
+ if (after) after(eventType, data as T, visible);
53
+ },
54
+ label: t(docname),
55
+ });
56
+ };