Coupled paraterized dictionary
This commit is contained in:
@@ -40,7 +40,8 @@ class ProvisionTemplateReference(BaseModel):
|
||||
"schema": "ProvisionTemplate",
|
||||
"displayedFields": ['title', 'body']
|
||||
},
|
||||
}
|
||||
},
|
||||
props={"parametrized": True}
|
||||
)
|
||||
|
||||
|
||||
@@ -56,6 +57,9 @@ class ContractTemplate(CrudDocument):
|
||||
default=[],
|
||||
props={"items-per-row": "1", "numbered": True}
|
||||
)
|
||||
variables: List[DictionaryEntry] = Field(default=[], format="dictionary")
|
||||
variables: List[DictionaryEntry] = Field(
|
||||
default=[],
|
||||
format="dictionary",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List
|
||||
|
||||
from .models import ContractTemplate, ProvisionTemplate, PartyTemplate, ProvisionTemplateReference
|
||||
from .models import ContractTemplate, ProvisionTemplate, PartyTemplate, ProvisionTemplateReference, DictionaryEntry
|
||||
from ..core.schemas import Writer
|
||||
from ..core.models import text_area
|
||||
|
||||
@@ -17,6 +17,11 @@ class ContractTemplateCreate(Writer):
|
||||
class ContractTemplateUpdate(BaseModel):
|
||||
name: str
|
||||
parties: List[PartyTemplate] = []
|
||||
variables: List[DictionaryEntry] = Field(
|
||||
default=[],
|
||||
format="dictionary",
|
||||
props={"required": False}
|
||||
)
|
||||
provisions: List[ProvisionTemplateReference] = []
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ export class CardComponent implements OnInit {
|
||||
@Input() is_modal: Boolean = false;
|
||||
|
||||
@Output() resourceCreated: EventEmitter<string> = new EventEmitter();
|
||||
@Output() resourceUpdated: EventEmitter<string> = new EventEmitter();
|
||||
@Output() resourceDeleted: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
|
||||
@@ -81,6 +82,7 @@ export class CardComponent implements OnInit {
|
||||
this.crudService.update(this.resource!, model).subscribe((model: any) => {
|
||||
this.model = model;
|
||||
this._modelLoading$.next(false);
|
||||
this.resourceUpdated.emit(model._id)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ export class CrudFormlyJsonschemaOptions implements FormlyJsonschemaOptions {
|
||||
field.type = "date";
|
||||
} else if (schema.hasOwnProperty('enum') && schema.enum.length == 1 && schema.enum[0] == schema.default ) {
|
||||
field.type = "hidden";
|
||||
} else if (schema.type == "array" && schema.format == "dictionary") {
|
||||
field.type = "dictionary";
|
||||
}
|
||||
|
||||
if (schema.hasOwnProperty('props')) {
|
||||
|
||||
@@ -20,6 +20,8 @@ import { JsonschemasService } from "./jsonschemas.service";
|
||||
import { MultiSchemaTypeComponent } from "./types/multischema.type";
|
||||
import { ForeignkeyTypeComponent } from "./types/foreignkey.type";
|
||||
import {HiddenTypeComponent} from "./types/hidden.type";
|
||||
import {DictionaryTypeComponent} from "./types/dictionary.type";
|
||||
import {DictionaryService} from "./types/dictionary.service";
|
||||
|
||||
|
||||
@NgModule({
|
||||
@@ -33,8 +35,15 @@ import {HiddenTypeComponent} from "./types/hidden.type";
|
||||
MultiSchemaTypeComponent,
|
||||
ForeignkeyTypeComponent,
|
||||
HiddenTypeComponent,
|
||||
DictionaryTypeComponent
|
||||
],
|
||||
providers: [
|
||||
JsonschemasService,
|
||||
ApiService,
|
||||
CrudService,
|
||||
CrudFormlyJsonschemaService,
|
||||
DictionaryService
|
||||
],
|
||||
providers: [ JsonschemasService, ApiService, CrudService, CrudFormlyJsonschemaService ],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HttpClientModule,
|
||||
@@ -51,6 +60,7 @@ import {HiddenTypeComponent} from "./types/hidden.type";
|
||||
{ name: 'multischema', component: MultiSchemaTypeComponent },
|
||||
{ name: 'foreign-key', component: ForeignkeyTypeComponent },
|
||||
{ name: 'hidden', component: HiddenTypeComponent },
|
||||
{ name: 'dictionary', component: DictionaryTypeComponent },
|
||||
]
|
||||
}),
|
||||
FormlyBootstrapModule
|
||||
|
||||
23
front/app/src/common/crud/types/dictionary.service.ts
Normal file
23
front/app/src/common/crud/types/dictionary.service.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {Observable, Subject} from "rxjs";
|
||||
|
||||
@Injectable()
|
||||
export class DictionaryService {
|
||||
fieldsParameters: {[field_id: string]: string[]} = {}
|
||||
parameters: string[] = []
|
||||
parameters$: Subject<string[]> = new Subject<string[]>()
|
||||
|
||||
updateParameters(field_id: string, newParameters: string[]) {
|
||||
this.fieldsParameters[field_id] = newParameters;
|
||||
|
||||
this.parameters = [].concat();
|
||||
for (const field of Object.values(this.fieldsParameters)) {
|
||||
this.parameters = this.parameters.concat(field);
|
||||
}
|
||||
this.parameters = this.parameters.filter(
|
||||
(item,index) => this.parameters.indexOf(item) === index
|
||||
);
|
||||
|
||||
this.parameters$.next(this.parameters)
|
||||
}
|
||||
}
|
||||
97
front/app/src/common/crud/types/dictionary.type.ts
Normal file
97
front/app/src/common/crud/types/dictionary.type.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import {FieldType, FieldTypeConfig, FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core';
|
||||
import {FormControl, FormGroup, FormArray} from "@angular/forms";
|
||||
import {DictionaryService} from "./dictionary.service";
|
||||
import {JSONSchema7} from "json-schema";
|
||||
import {FormlyJsonschema} from "@ngx-formly/core/json-schema";
|
||||
|
||||
@Component({
|
||||
selector: 'formly-dictionary-type',
|
||||
template: `
|
||||
<div class="mb-3">
|
||||
<ngb-accordion #acc="ngbAccordion" activeIds="ngb-panel-0">
|
||||
<ngb-panel>
|
||||
<ng-template ngbPanelTitle>
|
||||
<label *ngIf="props.label && props['hideLabel'] !== true" [attr.for]="id"
|
||||
class="form-label">{{ props.label }}
|
||||
<span *ngIf="props.required && props['hideRequiredMarker'] !== true" aria-hidden="true">*</span>
|
||||
</label>
|
||||
</ng-template>
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="alert alert-danger" role="alert" *ngIf="showError && formControl.errors">
|
||||
<formly-validation-message [field]="field"></formly-validation-message>
|
||||
</div>
|
||||
<formly-form [model]="parameterModel" [fields]="parameterFields" [form]="parameterForm"></formly-form>
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
</ngb-accordion>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class DictionaryTypeComponent extends FieldType<FieldTypeConfig> implements OnInit
|
||||
{
|
||||
public errorMsg: string = "";
|
||||
|
||||
parameterForm: FormGroup = new FormGroup({});
|
||||
parameterList: string[] = [];
|
||||
parameterModel: {[field_id: string]: string} = {};
|
||||
parameterFields: FormlyFieldConfig[] = [];
|
||||
|
||||
constructor(protected service: DictionaryService,
|
||||
private formlyJsonschema: FormlyJsonschema) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
for (const entry of this.formControl.value) {
|
||||
this.parameterModel[entry.key] = entry.value;
|
||||
}
|
||||
|
||||
this.service.parameters$.subscribe((parameters) => {
|
||||
this.parameterList = parameters;
|
||||
for (const pname of parameters) {
|
||||
if (Object.keys(this.parameterModel).indexOf(pname) == -1) {
|
||||
this.parameterModel[pname] = "";
|
||||
}
|
||||
}
|
||||
|
||||
this.parameterFields = [this.formlyJsonschema.toFieldConfig(
|
||||
this.getFormSchema(parameters)
|
||||
)];
|
||||
this.parameterForm.setValue(this.parameterModel, {emitEvent: false});
|
||||
});
|
||||
|
||||
this.parameterForm.valueChanges.subscribe((values) => {
|
||||
this.parameterModel = values;
|
||||
|
||||
const formValue: { key :string, value: string }[] = []
|
||||
for (const [key, value] of Object.entries(this.parameterModel)) {
|
||||
formValue.push({key, value});
|
||||
}
|
||||
this.formControl.setValue(formValue)
|
||||
})
|
||||
}
|
||||
|
||||
getFormSchema(properties: string[]) {
|
||||
const schema: JSONSchema7 = {
|
||||
type: "object",
|
||||
properties: {
|
||||
},
|
||||
};
|
||||
let required: string[] = []
|
||||
for (const pname of properties) {
|
||||
schema.properties![pname] = {
|
||||
type: "string",
|
||||
title: pname
|
||||
}
|
||||
if (this.field.props.required) {
|
||||
required.push(pname)
|
||||
}
|
||||
}
|
||||
schema.required = required
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { CrudService, Filters } from "../crud.service";
|
||||
import { CrudFormlyJsonschemaService } from "../crud-formly-jsonschema.service";
|
||||
import { FormGroup } from "@angular/forms";
|
||||
import {DictionaryService} from "./dictionary.service";
|
||||
|
||||
@Component({
|
||||
selector: 'formly-foreignkey-type',
|
||||
@@ -61,13 +62,14 @@ import { FormGroup } from "@angular/forms";
|
||||
[resource_id]="this.foreignModel._id"
|
||||
[schema]="this.foreignSchema"
|
||||
[is_modal]="true"
|
||||
(resourceDeleted)="onResourceDeleted($event)">
|
||||
(resourceDeleted)="onResourceDeleted($event)"
|
||||
(resourceUpdated)="onResourceModified($event)">
|
||||
</crud-card>
|
||||
<crud-card *ngIf="! this.hasValue()"
|
||||
[resource]="this.foreignResource"
|
||||
[schema]="this.foreignSchema"
|
||||
[is_modal]="true"
|
||||
(resourceCreated)="onResourceCreated($event)">
|
||||
(resourceCreated)="onResourceModified($event)">
|
||||
</crud-card>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -88,7 +90,8 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
|
||||
|
||||
constructor(private crudService: CrudService,
|
||||
private formlyJsonschema: CrudFormlyJsonschemaService,
|
||||
private modalService: NgbModal
|
||||
private modalService: NgbModal,
|
||||
protected service: DictionaryService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -114,18 +117,12 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
|
||||
this.foreignModel = {};
|
||||
this.foreignLabel = "";
|
||||
} else if (v != this.foreignModel._id) {
|
||||
this.getResource(v).pipe(
|
||||
map(v => { this.setModel(v); return v; }),
|
||||
map( v => this.formControl.patchValue(v._id))
|
||||
).subscribe()
|
||||
this.loadModel(v);
|
||||
}
|
||||
})
|
||||
).subscribe();
|
||||
if (this.hasValue()) {
|
||||
this.getResource(this.formControl.value).pipe(
|
||||
map( v => { this.setModel(v); return v; }),
|
||||
map( v => this.formControl.patchValue(v._id))
|
||||
).subscribe()
|
||||
this.loadModel(this.formControl.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +130,13 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
|
||||
return this.crudService.get(this.foreignResource, id);
|
||||
}
|
||||
|
||||
loadModel(id:string) {
|
||||
this.getResource(id).pipe(
|
||||
map(v => { this.setModel(v); return v; }),
|
||||
map( v => this.formControl.patchValue(v._id))
|
||||
).subscribe()
|
||||
}
|
||||
|
||||
search: OperatorFunction<string, readonly string[]> = (text$: Observable<string>) => {
|
||||
return text$.pipe(
|
||||
debounceTime(200),
|
||||
@@ -163,6 +167,7 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
|
||||
this.foreignModel = model;
|
||||
this.foreignLabel = model.label;
|
||||
this.loadDisplayedFields();
|
||||
this.service.updateParameters(this.field.id!, this.getParameters())
|
||||
}
|
||||
|
||||
hasValue() {
|
||||
@@ -173,9 +178,9 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
|
||||
this.modalService.open(modal, { size: 'xl' });
|
||||
}
|
||||
|
||||
onResourceCreated(resource_id: string) {
|
||||
onResourceModified(resource_id: string) {
|
||||
this.modalService.dismissAll();
|
||||
this.getResource(resource_id);
|
||||
this.loadModel(resource_id);
|
||||
}
|
||||
|
||||
see(modal: any) {
|
||||
@@ -213,4 +218,26 @@ export class ForeignkeyTypeComponent extends FieldType<FieldTypeConfig> implemen
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getParameters() {
|
||||
return this.extractParameters(this.foreignModel);
|
||||
}
|
||||
|
||||
extractParameters(obj: any) {
|
||||
let result: string[] = [];
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
if (typeof(obj[k]) == "string") {
|
||||
const matches = obj[k].match(/%[^\s.]+%/g);
|
||||
if (matches) {
|
||||
result = result.concat(matches.map((p: string | any[]) => p.slice(1,-1)) || []);
|
||||
}
|
||||
|
||||
result = result.concat();
|
||||
} else if (typeof(obj[k]) == "object") {
|
||||
result = result.concat(this.extractParameters(obj[k]));
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user