working frontend build
This commit is contained in:
@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<div class="{{component}}-editor">
|
||||
<div class="property-row">
|
||||
<label>Grant Name: </label>
|
||||
<div>grant.name</div>
|
||||
</div>
|
||||
|
||||
<div class="{{component}}-details">
|
||||
<!-- SYSTEM-BUILDER-property-row -->
|
||||
|
||||
</div>
|
||||
@ -13,10 +8,36 @@
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator';
|
||||
import { {{Component}}Config } from './{{component}}Types.ts';
|
||||
import { {{component}}Service } from './{{component}}Service.ts';
|
||||
|
||||
@Component
|
||||
export default class {{Component}}Editor extends Vue {
|
||||
@Prop() private {{component}}!: {{Component}}Config;
|
||||
@Prop() private id!: string;
|
||||
private {{component}}!: {{Component}}Config = {
|
||||
// SYSTEM-BUILDER-init-props
|
||||
};
|
||||
private loading: boolean = false;
|
||||
private errorMessage: string = '';
|
||||
|
||||
private mounted() {
|
||||
this.loadDetails();
|
||||
}
|
||||
|
||||
private loadDetails() {
|
||||
this.loading = true;
|
||||
this.errorMessage = '';
|
||||
{{component}}Service.get{{Component}}(this.id).then((res: {{Component}}Config) => {
|
||||
this.loading = false;
|
||||
this.errorMessage = '';
|
||||
this.{{component}} = res;
|
||||
}).catch(this.handleError.bind(this));
|
||||
}
|
||||
|
||||
private handleError(err: any) {
|
||||
// error
|
||||
this.loading = false;
|
||||
this.errorMessage = err.message;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -1,9 +1,5 @@
|
||||
<template>
|
||||
<div class="{{component}}-editor">
|
||||
<div class="input-row">
|
||||
<label>Grant Name: </label>
|
||||
<input class="form-control" type="text" ref="name" placeholder="Grant Name" v-model="grant.name" />
|
||||
</div>
|
||||
|
||||
<!-- SYSTEM-BUILDER-input-row -->
|
||||
|
||||
@ -22,7 +18,10 @@ import { {{Component}}Config } from './{{component}}Types.ts';
|
||||
|
||||
@Component
|
||||
export default class {{Component}}Editor extends Vue {
|
||||
@Prop() private {{component}}!: {{Component}}Config;
|
||||
@Prop() private id!: string;
|
||||
private {{component}}!: {{Component}}Config = {
|
||||
// SYSTEM-BUILDER-init-props
|
||||
};
|
||||
private loading: boolean = false;
|
||||
private errorMessage: string = '';
|
||||
|
||||
@ -30,11 +29,29 @@ export default class {{Component}}Editor extends Vue {
|
||||
return true;
|
||||
}
|
||||
|
||||
private close() {
|
||||
this.$emit('close-editor');
|
||||
}
|
||||
|
||||
private mounted() {
|
||||
this.loadDetails();
|
||||
}
|
||||
|
||||
private loadDetails() {
|
||||
this.loading = true;
|
||||
this.errorMessage = '';
|
||||
{{component}}Service.get{{Component}}(this.id).then((res: {{Component}}Config) => {
|
||||
this.loading = false;
|
||||
this.errorMessage = '';
|
||||
this.{{component}} = res;
|
||||
}).catch(this.handleError.bind(this));
|
||||
}
|
||||
|
||||
private save{{Component}}() {
|
||||
this.loading = true;
|
||||
this.errorMessage = '';
|
||||
// either create or update...
|
||||
if (this.{{component}}.id === '') { // TODO: use the primary key instead of 'id' here
|
||||
if (this.{{component}}.{{component}}_id === '') {
|
||||
{{component}}Service.create{{Component}}(this.{{component}}).then(() => {
|
||||
// success
|
||||
this.loading = false;
|
||||
|
@ -1,12 +1,15 @@
|
||||
<template>
|
||||
<div class="{{component}}-editor">
|
||||
<div>TODO: search</div>
|
||||
|
||||
<div v-for="item in items" :key="item.id" class="input-row">
|
||||
<div class="">{{ item.id }}</div>
|
||||
<div class="filter-row">
|
||||
<!-- SYSTEM-BUILDER-filter-options -->
|
||||
</div>
|
||||
|
||||
<div class="errorMessage" v-if="errorMessage != ''">{{errorMessage}}</div>
|
||||
<div v-for="item in items" :key="item.{{component}}_id" class="input-row">
|
||||
<div class="">{{ item.{{component}}_id }}</div>
|
||||
<!-- SYSTEM-BUILDER-item-props -->
|
||||
</div>
|
||||
|
||||
<div class="red-text" v-if="errorMessage !== ''">{{errorMessage}}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -17,22 +20,18 @@ import { {{Component}}Config } from './{{component}}Types.ts';
|
||||
|
||||
@Component
|
||||
export default class {{Component}}Editor extends Vue {
|
||||
private items!: {{Component}}Config[];
|
||||
private items: {{Component}}Config[] = [];
|
||||
private loading: boolean = false;
|
||||
private errorMessage: string = '';
|
||||
private filterItems: {{Component}}Config = {
|
||||
// SYSTEM-BUILDER-init-props
|
||||
};
|
||||
// SYSTEM-BUILDER-component-variables
|
||||
|
||||
// SYSTEM-BUILDER-component-functions
|
||||
|
||||
private get{{Component}}s() {
|
||||
this.loading = true;
|
||||
this.errorMessage = '';
|
||||
{{component}}Service.get{{Component}}s().then((listItems: {{Component}}Config[]) => {
|
||||
// success
|
||||
this.loading = false;
|
||||
this.errorMessage = '';
|
||||
this.items = listItems;
|
||||
}).catch(this.handleError.bind(this));
|
||||
private mounted() {
|
||||
get{{Components}}();
|
||||
}
|
||||
|
||||
// TODO: add a search function if there is an endpoint with type 'search'
|
||||
@ -46,7 +45,17 @@ export default class {{Component}}Editor extends Vue {
|
||||
</script>
|
||||
|
||||
<style scoped lang="css">
|
||||
.errorMessage {
|
||||
.red-text {
|
||||
color: red;
|
||||
}
|
||||
.input-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.filter-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
|
@ -2,9 +2,12 @@
|
||||
// SYSTEM-BUILDER-{{component}}-interfaces
|
||||
|
||||
// e.g.
|
||||
// interface {{Component}}Config {
|
||||
// }
|
||||
interface {{Component}}Config {
|
||||
{{component}}_id: string;
|
||||
// SYSTEM-BUILDER-init-props
|
||||
}
|
||||
|
||||
export {
|
||||
{{Component}}Config,
|
||||
// SYSTEM-BUILDER-{{component}}-exports
|
||||
};
|
61
src/frontend-services/details-view-builder.ts
Normal file
61
src/frontend-services/details-view-builder.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import {ColumnDef, ColumnRef, ComponentDef, EndpointDef, SystemDef, TableDef} from "../systemGenService";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import {createDetailsInitValues} from "../helpers";
|
||||
|
||||
export function buildDetailsView(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
|
||||
let detailsEndpoints: EndpointDef[] = component.endpoints.filter((epd: EndpointDef) => {
|
||||
return epd.type === 'item';
|
||||
});
|
||||
if (detailsEndpoints.length) {
|
||||
return insertFEDetailsMarkup(detailsEndpoints[0], outDir).then(() => {
|
||||
return insertFEDetailsCode(detailsEndpoints[0], outDir, systemDef);
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function insertFEDetailsCode(view: EndpointDef, outDir: string, systemDef: SystemDef) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `// SYSTEM-BUILDER-init-props`;
|
||||
let fileLocation = path.join(outDir, `${view.component}Details.vue`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createDetailsInitValues(view, systemDef);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function insertFEDetailsMarkup(view: EndpointDef, outDir: string) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `<!-- SYSTEM-BUILDER-property-row -->`;
|
||||
let fileLocation = path.join(outDir, `${view.component}Details.vue`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createDetailsMarkup(view);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function createDetailsMarkup(view: EndpointDef): string {
|
||||
let out = ``;
|
||||
|
||||
view.columns.forEach((column: ColumnRef) => {
|
||||
out = out + `<div class="property-row">
|
||||
<label>${column.name}: </label>
|
||||
<div>{{ ${view.component}.${column.name} }}</div>
|
||||
</div>
|
||||
|
||||
`;
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
73
src/frontend-services/editor-view-builder.ts
Normal file
73
src/frontend-services/editor-view-builder.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import {ColumnDef, ColumnRef, ComponentDef, EndpointDef, SystemDef, TableDef} from "../systemGenService";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import {createDetailsInitValues, uppercaseFirstLetter} from "../helpers";
|
||||
|
||||
export function buildEditorView(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
|
||||
let detailsEndpoints: EndpointDef[] = component.endpoints.filter((epd: EndpointDef) => {
|
||||
return epd.type === 'item';
|
||||
});
|
||||
if (detailsEndpoints.length) {
|
||||
return insertFEDetailsMarkup(detailsEndpoints[0], outDir).then(() => {
|
||||
return insertFEDetailsCode(detailsEndpoints[0], outDir, systemDef);
|
||||
});
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function insertFEDetailsCode(view: EndpointDef, outDir: string, systemDef: SystemDef) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `// SYSTEM-BUILDER-init-props`;
|
||||
let fileLocation = path.join(outDir, `${view.component}Editor.vue`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createDetailsInitValues(view, systemDef);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function insertFEDetailsMarkup(view: EndpointDef, outDir: string) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `<!-- SYSTEM-BUILDER-input-row -->`;
|
||||
let fileLocation = path.join(outDir, `${view.component}Editor.vue`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createDetailsMarkup(view);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function createDetailsMarkup(view: EndpointDef): string {
|
||||
let out = ``;
|
||||
let inputType: {[key: string]: string} = {
|
||||
"blob": "text",
|
||||
"boolean": "checkbox",
|
||||
"date": "date",
|
||||
"dateTIME": "datetime-local",
|
||||
"number": "number",
|
||||
"string": "text",
|
||||
};
|
||||
|
||||
view.columns.forEach((column: ColumnRef) => {
|
||||
let inputTypeValue = 'text';
|
||||
if (column.type) {
|
||||
inputTypeValue = inputType[column.type];
|
||||
}
|
||||
out = out + `<div class="input-row">
|
||||
<label>${uppercaseFirstLetter(view.component)} ${uppercaseFirstLetter(column.name)}: </label>
|
||||
<input class="form-control" type="${inputTypeValue}" placeholder="${column.name}" v-model="${view.component}.${column.name}" />
|
||||
</div>
|
||||
|
||||
`;
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,18 +1,27 @@
|
||||
import {ComponentDef, EndpointDef, SystemDef} from "../systemGenService";
|
||||
import path from "path";
|
||||
import {initializeComponentFile} from "../helpers";
|
||||
import {initializeComponentFile, removeTemplateFiles} from "../helpers";
|
||||
import fs from "fs";
|
||||
import {buildServiceFunctionName, getFuncParams} from "../views/service-creator";
|
||||
import {METHOD, URL} from "../views/routes-creator";
|
||||
import {buildDetailsView} from "./details-view-builder";
|
||||
import {buildListView} from "./list-view-builder";
|
||||
import {buildEditorView} from "./editor-view-builder";
|
||||
import {buildTypesView} from "./types-view-builder";
|
||||
const ncp = require('ncp').ncp;
|
||||
|
||||
export function createFrontend(systemDef: SystemDef) {
|
||||
export function createFrontend(systemDef: SystemDef): Promise<void[]> {
|
||||
let fePromises = [];
|
||||
for (let i in systemDef.components) {
|
||||
fePromises.push(createComponent(systemDef.components[i], systemDef));
|
||||
// TODO: add a vue view and add it to App.vue
|
||||
// TODO: build app router logic
|
||||
}
|
||||
return Promise.all(fePromises);
|
||||
// TODO: after all is done clean up the template files
|
||||
return Promise.all(fePromises).then((res: void[]) => {
|
||||
removeTemplateFiles(componentSource())
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
function componentSource() {
|
||||
@ -38,12 +47,57 @@ function createComponent(component: ComponentDef, systemDef: SystemDef) {
|
||||
return insertFEServiceCode(component.endpoints[i], componentDest);
|
||||
});
|
||||
// TODO: use templates for Details, Editor, List and Types view based on the endpoints provided
|
||||
// servicePromise = servicePromise.then(() => {
|
||||
//
|
||||
// return initializeComponentFile(path.join(componentDest, '{{component}}Service.ets'), path.join(componentDest, `${component.component}Service.ts`), component.component, systemDef.name);
|
||||
// });
|
||||
}
|
||||
if (componentContains('list', component)) {
|
||||
servicePromise = servicePromise.then(() => {
|
||||
return initializeComponentFile(path.join(componentDest, '{{component}}List.vue'), path.join(componentDest, `${component.component}List.vue`), component.component, systemDef.name);
|
||||
}).then(() => {
|
||||
// TODO: build list contents based on the list endpoint
|
||||
return buildListView(component, componentDest, systemDef);
|
||||
});
|
||||
}
|
||||
if (componentContains('item', component)) {
|
||||
servicePromise = servicePromise.then(() => {
|
||||
return initializeComponentFile(path.join(componentDest, '{{component}}Details.vue'), path.join(componentDest, `${component.component}Details.vue`), component.component, systemDef.name);
|
||||
}).then(() => {
|
||||
// build list contents based on the item endpoint
|
||||
return buildDetailsView(component, componentDest, systemDef);
|
||||
});
|
||||
}
|
||||
if (componentContains('update', component) || componentContains('create', component)) {
|
||||
servicePromise = servicePromise.then(() => {
|
||||
return initializeComponentFile(path.join(componentDest, '{{component}}Editor.vue'), path.join(componentDest, `${component.component}Editor.vue`), component.component, systemDef.name);
|
||||
}).then(() => {
|
||||
// build list contents based on the item endpoint
|
||||
return buildEditorView(component, componentDest, systemDef);
|
||||
});
|
||||
}
|
||||
if (component.endpoints.length) {
|
||||
// add a types file to power the whole component
|
||||
servicePromise = servicePromise.then(() => {
|
||||
return initializeComponentFile(path.join(componentDest, '{{component}}Types.ets'), path.join(componentDest, `${component.component}Types.ts`), component.component, systemDef.name);
|
||||
}).then(() => {
|
||||
return buildTypesView(component, componentDest, systemDef);
|
||||
});
|
||||
}
|
||||
servicePromise.then(() => {
|
||||
componentResolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function componentContains(type: string, component: ComponentDef) {
|
||||
return component.endpoints.some((epd: EndpointDef) => {
|
||||
return epd.type === type;
|
||||
});
|
||||
}
|
||||
|
||||
function insertFEServiceCode(view: EndpointDef, outDir: string) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `// SYSTEM-BUILDER-service-functions`;
|
||||
|
219
src/frontend-services/list-view-builder.ts
Normal file
219
src/frontend-services/list-view-builder.ts
Normal file
@ -0,0 +1,219 @@
|
||||
import {ColumnDef, ColumnRef, ComponentDef, EndpointDef, Filter, SystemDef, TableDef} from "../systemGenService";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import {createDetailsInitValues, findColumn, makeSet, uppercaseFirstLetter} from "../helpers";
|
||||
import pluralize from "pluralize";
|
||||
import {getFuncParams} from "../views/service-creator";
|
||||
|
||||
const INIT_VALUE = {
|
||||
"blob": "''",
|
||||
"boolean": "false",
|
||||
"date": "''",
|
||||
"dateTIME": "''",
|
||||
"number": "0",
|
||||
"string": "''"
|
||||
};
|
||||
|
||||
export function buildListView(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
|
||||
let hasDeleteEndpoint = false;
|
||||
let hasItemEndpoint = false;
|
||||
let hasUpdateEndpoint = false;
|
||||
let hasCreateEndpoint = false;
|
||||
let listEndpoints: EndpointDef[] = component.endpoints.filter((epd: EndpointDef) => {
|
||||
if (epd.type === 'delete') {
|
||||
hasDeleteEndpoint = true;
|
||||
}
|
||||
if (epd.type === 'create') {
|
||||
hasCreateEndpoint = true;
|
||||
}
|
||||
if (epd.type === 'update') {
|
||||
hasUpdateEndpoint = true;
|
||||
}
|
||||
if (epd.type === 'item') {
|
||||
hasItemEndpoint = true;
|
||||
}
|
||||
return epd.type === 'list' || epd.type === 'search';
|
||||
});
|
||||
let filterParams: Filter[] = [];
|
||||
let endpointPromise = Promise.resolve();
|
||||
for (let i in listEndpoints) {
|
||||
if (i === '0') {
|
||||
insertFEPropsCode(listEndpoints[i], outDir, systemDef);
|
||||
}
|
||||
if (listEndpoints[i].filters) {
|
||||
filterParams = [...filterParams, ...listEndpoints[i].filters as Filter[]];
|
||||
}
|
||||
endpointPromise = endpointPromise.then(() => {
|
||||
return insertFEListCode(listEndpoints[i], outDir, systemDef, hasDeleteEndpoint);
|
||||
});
|
||||
}
|
||||
if (listEndpoints.length) {
|
||||
endpointPromise = endpointPromise.then(() => {
|
||||
return insertFEListMarkup(listEndpoints[0], outDir, hasDeleteEndpoint)
|
||||
});
|
||||
}
|
||||
if (hasDeleteEndpoint && listEndpoints.length) {
|
||||
endpointPromise = endpointPromise.then(() => {
|
||||
return insertFEDeleteCode(listEndpoints[0], outDir);
|
||||
});
|
||||
}
|
||||
// combine all duplicate filterParams
|
||||
// @ts-ignore
|
||||
let filterSet: Filter[] = makeSet<Filter>(filterParams, 'param');
|
||||
// add filterParams to the markup as filter options
|
||||
if (listEndpoints.length) {
|
||||
endpointPromise = endpointPromise.then(() => {
|
||||
return insertFEFiltersMarkup(listEndpoints[0], filterSet, outDir);
|
||||
});
|
||||
}
|
||||
return endpointPromise;
|
||||
}
|
||||
|
||||
function insertFEListCode(view: EndpointDef, outDir: string, systemDef: SystemDef, hasDeleteEndpoint: boolean) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `// SYSTEM-BUILDER-component-functions`;
|
||||
let fileLocation = path.join(outDir, `${view.component}List.vue`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createListRequestFunc(view, systemDef, hasDeleteEndpoint);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function insertFEDeleteCode(view: EndpointDef, outDir: string) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `// SYSTEM-BUILDER-component-functions`;
|
||||
let fileLocation = path.join(outDir, `${view.component}List.vue`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createFEDeleteCode(view);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function insertFEListMarkup(view: EndpointDef, outDir: string, hasDeleteEndpoint: boolean) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `<!-- SYSTEM-BUILDER-item-props -->`;
|
||||
let fileLocation = path.join(outDir, `${view.component}List.vue`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createListMarkup(view, hasDeleteEndpoint);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function insertFEFiltersMarkup(view: EndpointDef, filters: Filter[], outDir: string) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `<!-- SYSTEM-BUILDER-filter-options -->`;
|
||||
let fileLocation = path.join(outDir, `${view.component}List.vue`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createFilterMarkup(view, filters);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function insertFEPropsCode(view: EndpointDef, outDir: string, systemDef: SystemDef) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `// SYSTEM-BUILDER-init-props`;
|
||||
let fileLocation = path.join(outDir, `${view.component}List.vue`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createDetailsInitValues(view, systemDef);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
function createFilterMarkup(view: EndpointDef, filters: Filter[]): string {
|
||||
let out = ``;
|
||||
let inputType: {[key: string]: string} = {
|
||||
"blob": "text",
|
||||
"boolean": "checkbox",
|
||||
"date": "date",
|
||||
"dateTIME": "datetime-local",
|
||||
"number": "number",
|
||||
"string": "text",
|
||||
};
|
||||
filters.forEach((f: Filter) => {
|
||||
if (f.type) {
|
||||
// base the filter markup on type
|
||||
out = out + `<div>
|
||||
<input type="${inputType[f.type] || 'text'}" placeholder="${f.param}" v-model="item.${f.param}"/>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
// default to a string filter
|
||||
out = out + `<div>
|
||||
<input type="text" placeholder="${f.param}" v-model="item.${f.param}"/>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
});
|
||||
out = out + `<div>
|
||||
<button @click="search${uppercaseFirstLetter(pluralize(view.component))}()">Search</button>
|
||||
</div>
|
||||
`;
|
||||
return out;
|
||||
}
|
||||
|
||||
function createListMarkup(view: EndpointDef, hasDeleteEndpoint: boolean): string {
|
||||
let out = ``;
|
||||
|
||||
view.columns.forEach((column: ColumnRef) => {
|
||||
out = out + `<div class="">{{ item.${column.name} }}</div>
|
||||
`;
|
||||
});
|
||||
if (hasDeleteEndpoint) {
|
||||
out = out + `<div class="red-text" @click="deleteItem(item.${view.component}_id)">delete</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function createListRequestFunc(view: EndpointDef, systemDef: SystemDef, hasDeleteEndpoint: boolean) {
|
||||
let isSearch: boolean = view.type === 'search';
|
||||
let params: string[] = getFuncParams(view);
|
||||
params.push('0'); // offset
|
||||
params.push('1000'); // limit
|
||||
|
||||
let out = `private ${isSearch ? 'search' : 'get'}${uppercaseFirstLetter(pluralize(view.component))}() {
|
||||
this.loading = true;
|
||||
this.errorMessage = '';
|
||||
${view.component}Service.${isSearch ? 'search' : 'get'}${uppercaseFirstLetter(pluralize(view.component))}(${params.join(', ')}).then((listItems: ${view.component}Config[]) => {
|
||||
// success
|
||||
this.loading = false;
|
||||
this.errorMessage = '';
|
||||
this.items = listItems;
|
||||
}).catch(this.handleError.bind(this));
|
||||
}
|
||||
|
||||
`;
|
||||
return out;
|
||||
}
|
||||
|
||||
function createFEDeleteCode(view: EndpointDef) {
|
||||
let out = `private deleteItem(itemKey: string) {
|
||||
this.loading = true;
|
||||
this.errorMessage = '';
|
||||
${view.component}Service.delete${uppercaseFirstLetter(pluralize(view.component))}(itemKey).then(() => {
|
||||
this.loading = false;
|
||||
this.errorMessage = '';
|
||||
return this.get${uppercaseFirstLetter(pluralize(view.component))}();
|
||||
}).catch(this.handleError.bind(this));
|
||||
}
|
||||
|
||||
`;
|
||||
return out;
|
||||
}
|
||||
|
25
src/frontend-services/types-view-builder.ts
Normal file
25
src/frontend-services/types-view-builder.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {ComponentDef, EndpointDef, SystemDef} from "../systemGenService";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import {createDetailsTypeValues} from "../helpers";
|
||||
|
||||
export function buildTypesView(component: ComponentDef, outDir: string, systemDef: SystemDef): Promise<void> {
|
||||
if (component.endpoints.length) {
|
||||
return insertFEDetailsCode(component.endpoints[0], outDir, systemDef);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function insertFEDetailsCode(view: EndpointDef, outDir: string, systemDef: SystemDef) {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `// SYSTEM-BUILDER-init-props`;
|
||||
let fileLocation = path.join(outDir, `${view.component}Types.ts`)
|
||||
let initServiceFile: string = fs.readFileSync(fileLocation, 'utf8');
|
||||
let parts = initServiceFile.split(separator);
|
||||
parts[0] = parts[0] + createDetailsTypeValues(view, systemDef);
|
||||
let newServiceFile = parts.join(separator);
|
||||
fs.writeFileSync(fileLocation, newServiceFile, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
}
|
108
src/helpers.ts
108
src/helpers.ts
@ -1,7 +1,26 @@
|
||||
import fs from "fs";
|
||||
import {EndpointDef} from "./systemGenService";
|
||||
import {ColumnDef, ColumnRef, EndpointDef, Filter, SystemDef, TableDef} from "./systemGenService";
|
||||
import path from "path";
|
||||
import {createServiceFunc} from "./views/service-creator";
|
||||
import pluralize from "pluralize";
|
||||
|
||||
const INIT_VALUE = {
|
||||
"blob": "''",
|
||||
"boolean": "false",
|
||||
"date": "''",
|
||||
"dateTIME": "''",
|
||||
"number": "0",
|
||||
"string": "''"
|
||||
};
|
||||
|
||||
const TYPE_VALUE = {
|
||||
"blob": "string",
|
||||
"boolean": "boolean",
|
||||
"date": "string",
|
||||
"dateTIME": "string",
|
||||
"number": "number",
|
||||
"string": "string",
|
||||
};
|
||||
|
||||
function uppercaseFirstLetter(input: string) {
|
||||
return input.charAt(0).toUpperCase() + input.slice(1);
|
||||
@ -15,16 +34,32 @@ function initializeComponentFile(sourceFile: string, destinationFile: string, co
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.rename(sourceFile, destinationFile, (err: any) => {
|
||||
let fileContents = fs.readFileSync(destinationFile, 'utf8');
|
||||
var uppercaseFirstLetterComponentName = uppercaseFirstLetter(component);
|
||||
var lowercaseFirstLetterComponentName = lowercaseFirstLetter(component);
|
||||
let uppercaseFirstLetterComponentName = uppercaseFirstLetter(component);
|
||||
let lowercaseFirstLetterComponentName = lowercaseFirstLetter(component);
|
||||
let lcPluralComponentName = lowercaseFirstLetter(pluralize(component));
|
||||
let ucPluralComponentName = uppercaseFirstLetter(pluralize(component));
|
||||
let newFileContents = fileContents.split('{{Component}}').join(uppercaseFirstLetterComponentName);
|
||||
newFileContents = newFileContents.split('{{component}}').join(lowercaseFirstLetterComponentName);
|
||||
newFileContents = newFileContents.split('{{components}}').join(lcPluralComponentName);
|
||||
newFileContents = newFileContents.split('{{Components}}').join(ucPluralComponentName);
|
||||
fs.writeFileSync(destinationFile, newFileContents, 'utf8');
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function removeTemplateFiles(destinationFolder: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fs.rmdir(destinationFolder, (err) => {
|
||||
if (err) {
|
||||
reject();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function insertServiceCode(view: EndpointDef, outDir: string): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
const separator: string = `// SYSTEM-BUILDER-${view.component}-service`;
|
||||
@ -38,9 +73,70 @@ function insertServiceCode(view: EndpointDef, outDir: string): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
function findColumn(col: ColumnRef, systemDef: SystemDef): ColumnDef | undefined {
|
||||
let colDef: ColumnDef | undefined;
|
||||
let table: TableDef | undefined = systemDef.storage.tables.find((td: TableDef) => {
|
||||
return td.name === col.table;
|
||||
});
|
||||
if (table) {
|
||||
colDef = table.columns.find((cd: ColumnDef) => {
|
||||
return cd.name === col.name;
|
||||
});
|
||||
}
|
||||
return colDef;
|
||||
}
|
||||
|
||||
function makeSet<T extends {[id: string]: string}>(list: T[], id: string): T[] {
|
||||
let typeObject: {[key: string]: T} = {};
|
||||
for (let i in list) {
|
||||
if (!typeObject[list[i][id]]) {
|
||||
typeObject[list[i][id]] = list[i];
|
||||
}
|
||||
}
|
||||
let typeSet: T[] = [];
|
||||
for (let key in typeObject) {
|
||||
typeSet.push(typeObject[key]);
|
||||
}
|
||||
return typeSet;
|
||||
}
|
||||
|
||||
function createDetailsInitValues(view: EndpointDef, systemDef: SystemDef) {
|
||||
let out = `${view.component}_id: '',
|
||||
`;
|
||||
|
||||
view.columns.forEach((column: ColumnRef) => {
|
||||
let colDef: ColumnDef | undefined = findColumn(column, systemDef);
|
||||
if (colDef) {
|
||||
out = out + `${colDef.name}: ${INIT_VALUE[colDef.type]},
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function createDetailsTypeValues(view: EndpointDef, systemDef: SystemDef) {
|
||||
let out = ``;
|
||||
|
||||
view.columns.forEach((column: ColumnRef) => {
|
||||
let colDef: ColumnDef | undefined = findColumn(column, systemDef);
|
||||
if (colDef) {
|
||||
out = out + `${colDef.name}: ${TYPE_VALUE[colDef.type]};
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
export {
|
||||
uppercaseFirstLetter,
|
||||
lowercaseFirstLetter,
|
||||
createDetailsInitValues,
|
||||
createDetailsTypeValues,
|
||||
findColumn,
|
||||
initializeComponentFile,
|
||||
insertServiceCode
|
||||
insertServiceCode,
|
||||
lowercaseFirstLetter,
|
||||
makeSet,
|
||||
removeTemplateFiles,
|
||||
uppercaseFirstLetter,
|
||||
}
|
@ -3,6 +3,7 @@ const ncp = require('ncp').ncp;
|
||||
import { createDatabase, writeMigrationsToFile } from './database/database-creator';
|
||||
import { createViews } from './views/views-creator';
|
||||
import {createFrontend} from "./frontend-services/fe-service-creator";
|
||||
import {removeTemplateFiles} from "./helpers";
|
||||
|
||||
class SystemGenService {
|
||||
|
||||
@ -44,6 +45,7 @@ class SystemGenService {
|
||||
|
||||
public cleanup() {
|
||||
// TODO: remove 'component/{{component}}' folder
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +56,7 @@ interface SystemDef {
|
||||
name: string;
|
||||
storage: StorageDef;
|
||||
components: ComponentDef[];
|
||||
// TODO: add Views, ACLs, Behaviors, UX
|
||||
// TODO: add ACLs, Behaviors, UX
|
||||
}
|
||||
|
||||
interface StorageDef {
|
||||
|
Reference in New Issue
Block a user