@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.
- package/.claude/settings.local.json +10 -0
- package/ReleaseNote.md +9 -0
- package/dist/buildinschemas/autoincreament.d.ts +3 -0
- package/dist/buildinschemas/autoincreament.d.ts.map +1 -0
- package/dist/buildinschemas/autoincreament.js +39 -0
- package/dist/buildinschemas/autoincreament.js.map +1 -0
- package/dist/buildinschemas/docnoformat.d.ts +3 -0
- package/dist/buildinschemas/docnoformat.d.ts.map +1 -0
- package/dist/buildinschemas/docnoformat.js +58 -0
- package/dist/buildinschemas/docnoformat.js.map +1 -0
- package/dist/buildinschemas/documentevent.d.ts +3 -0
- package/dist/buildinschemas/documentevent.d.ts.map +1 -0
- package/dist/buildinschemas/documentevent.js +48 -0
- package/dist/buildinschemas/documentevent.js.map +1 -0
- package/dist/buildinschemas/webhooklog.d.ts +3 -0
- package/dist/buildinschemas/webhooklog.d.ts.map +1 -0
- package/dist/buildinschemas/webhooklog.js +79 -0
- package/dist/buildinschemas/webhooklog.js.map +1 -0
- package/dist/framework.d.ts.map +1 -1
- package/dist/framework.js +8 -18
- package/dist/framework.js.map +1 -1
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +35 -19
- package/dist/generate.js.map +1 -1
- package/dist/index.js +7 -17
- package/dist/index.js.map +1 -1
- package/dist/libs.d.ts.map +1 -1
- package/dist/processors/bpmnbuilder.d.ts.map +1 -1
- package/dist/processors/bpmnbuilder.js +7 -17
- package/dist/processors/bpmnbuilder.js.map +1 -1
- package/dist/processors/jrxmlbuilder.d.ts.map +1 -1
- package/dist/processors/jrxmlbuilder.js +7 -17
- package/dist/processors/jrxmlbuilder.js.map +1 -1
- package/dist/processors/jsonschemabuilder.d.ts.map +1 -1
- package/dist/processors/jsonschemabuilder.js +2 -0
- package/dist/processors/jsonschemabuilder.js.map +1 -1
- package/dist/type.d.ts +1 -0
- package/dist/type.d.ts.map +1 -1
- package/dist/type.js.map +1 -1
- package/package.json +3 -3
- package/src/generate.ts +27 -1
- package/src/processors/jsonschemabuilder.ts +1 -0
- package/src/type.ts +1 -0
- package/templates/basic/nest/default.ts.eta +61 -2
- package/templates/basic/nest/enum.ts.eta +86 -12
- package/templates/basic/nest/schema.ts.eta +58 -7
- package/templates/nest/src/simple-app/_core/features/profile/profile.service.ts.eta +15 -18
- package/templates/nest/src/simple-app/_core/framework/simple-app.interceptor.ts.eta +2 -1
- package/templates/nest/src/simple-app/_core/utils/string-utils.ts.eta +26 -3
- 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
|
-
<%
|
|
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
|
|
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 {
|
|
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 {
|
|
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: '
|
|
163
|
-
street2: '
|
|
164
|
-
postcode: '
|
|
165
|
-
city: '
|
|
159
|
+
street1: '',
|
|
160
|
+
street2: '',
|
|
161
|
+
postcode: '',
|
|
162
|
+
city: '',
|
|
166
163
|
tel: mobileNo,
|
|
167
164
|
email: appuser.getEmail(),
|
|
168
|
-
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
|
|
4
|
-
|
|
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
|
|
8
|
-
import { FormCrudEvent,SimpleAppDocumentType } from
|
|
9
|
-
export const getDocument = (docname:SimpleAppDocumentType) =>{
|
|
10
|
-
|
|
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>(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
+
};
|