-
@@ -81,7 +81,9 @@
@@ -143,6 +145,15 @@
>locked
+
+ 0"
+ (click)="create(element)"
+ >
+ save
+
+
|
diff --git a/src/app/pages/tables/bikes/bikes.component.ts b/src/app/pages/tables/bikes/bikes.component.ts
index cdb5ef4..139387a 100644
--- a/src/app/pages/tables/bikes/bikes.component.ts
+++ b/src/app/pages/tables/bikes/bikes.component.ts
@@ -4,12 +4,6 @@ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { BikesService, CargoBikeResult } from 'src/app/services/bikes.service';
import { flatten } from 'src/app/helperFunctions/flattenObject';
import { deepen } from 'src/app/helperFunctions/deepenObject';
-import { isPartOfGraphQLDoc } from 'src/app/helperFunctions/isPartOfGraphQLFunction';
-import { filter } from 'graphql-anywhere';
-import {
- CargoBikeFieldsMutableFragmentDoc,
- CargoBikeUpdateInput,
-} from 'src/generated/graphql';
import { SchemaService } from 'src/app/services/schema.service';
import { logArrayInColumnInfoForm } from 'src/app/helperFunctions/logArrayInColumnInfoForm';
@@ -27,6 +21,8 @@ export class BikesComponent {
columnInfo: {
name: string;
header: string;
+ acceptedForCreation?: boolean;
+ requiredForCreation?: boolean;
sticky?: boolean;
readonly?: boolean;
type?: string;
@@ -41,9 +37,18 @@ export class BikesComponent {
{ name: 'insuranceData.name', header: 'Versicherer' },
{ name: 'insuranceData.benefactor', header: 'Kostenträger' },
{ name: 'insuranceData.noPnP', header: 'Nr. P&P' },
- { name: 'insuranceData.maintenanceResponsible', header: 'Wartung zuständig' },
- { name: 'insuranceData.maintenanceBenefactor', header: 'Wartung Kostenträger' },
- { name: 'insuranceData.maintenanceAgreement', header: 'Wartungsvereinbarung' },
+ {
+ name: 'insuranceData.maintenanceResponsible',
+ header: 'Wartung zuständig',
+ },
+ {
+ name: 'insuranceData.maintenanceBenefactor',
+ header: 'Wartung Kostenträger',
+ },
+ {
+ name: 'insuranceData.maintenanceAgreement',
+ header: 'Wartungsvereinbarung',
+ },
{ name: 'insuranceData.projectAllowance', header: 'Projektzuschuss' },
{ name: 'insuranceData.notes', header: 'Sonstiges' },
{ name: 'dimensionsAndLoad.bikeLength', header: 'Länge' },
@@ -56,7 +61,10 @@ export class BikesComponent {
{ name: 'dimensionsAndLoad.hasCoverBox', header: 'Boxabdeckung j/n' },
{ name: 'dimensionsAndLoad.lockable', header: 'Box abschließbar' },
{ name: 'dimensionsAndLoad.maxWeightBox', header: 'max Zuladung Box' },
- { name: 'dimensionsAndLoad.maxWeightLuggageRack', header: 'max Zuladung Gepäckträger' },
+ {
+ name: 'dimensionsAndLoad.maxWeightLuggageRack',
+ header: 'max Zuladung Gepäckträger',
+ },
{ name: 'dimensionsAndLoad.maxWeightTotal', header: 'max Gesamtgewicht' },
{ name: 'numberOfChildren', header: 'Anzahl Kinder' },
{ name: 'numberOfWheels', header: 'Anzahl Räder' },
@@ -64,8 +72,14 @@ export class BikesComponent {
{ name: 'forChildren', header: 'für Kinder j/n' },
{ name: 'security.frameNumber', header: 'Rahmennummer' },
{ name: 'security.adfcCoding', header: 'ADFC Codierung' },
- { name: 'security.keyNumberAXAChain', header: 'Schlüsselnrummer Rahmenschloss' },
- { name: 'security.keyNumberFrameLock', header: 'Schlüsselnrummer AXA-Kette' },
+ {
+ name: 'security.keyNumberAXAChain',
+ header: 'Schlüsselnrummer Rahmenschloss',
+ },
+ {
+ name: 'security.keyNumberFrameLock',
+ header: 'Schlüsselnrummer AXA-Kette',
+ },
{ name: 'security.policeCoding', header: 'Polizei Codierung' },
{ name: 'technicalEquipment.bicycleShift', header: 'Schaltung' },
{ name: 'technicalEquipment.isEBike', header: 'E-Bike j/n' },
@@ -92,6 +106,10 @@ export class BikesComponent {
{ name: 'lendingStation.address.zip', header: '' },
];
+ tableDataGQLType: string = 'CargoBike';
+ tableDataGQLCreateInputType: string = 'CargoBikeCreateInput';
+ tableDataGQLUpdateInputType: string = 'CargoBikeUpdateInput';
+
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
@@ -116,9 +134,15 @@ export class BikesComponent {
) {}
ngAfterViewInit() {
- this.addTypesToColumnInfo();
- this.addReadOnlyPropertiesToColumnInfo();
+ this.addColumnPropertiesFromGQLSchemaToColumnInfo();
this.data.paginator = this.paginator;
+ this.data.sortingDataAccessor = (item, columnName) => {
+ if (typeof item[columnName] === 'string') {
+ return item[columnName].toLocaleLowerCase();
+ }
+
+ return item[columnName];
+ }
this.data.sort = this.sort;
this.columnInfo.forEach((column) =>
@@ -162,19 +186,28 @@ export class BikesComponent {
clearInterval(this.relockingInterval);
}
- addTypesToColumnInfo() {
+ addColumnPropertiesFromGQLSchemaToColumnInfo() {
for (const column of this.columnInfo) {
- if (!column.type) {
- column.type = this.getType(column.name);
- }
+ const typeInformation = this.schemaService.getTypeInformation(
+ this.tableDataGQLType,
+ column.name
+ );
+ column.type = column.type || typeInformation.type;
}
- }
-
- addReadOnlyPropertiesToColumnInfo() {
for (const column of this.columnInfo) {
- if (!column.readonly) {
- column.readonly = this.isReadonly(column.name);
- }
+ const typeInformation = this.schemaService.getTypeInformation(
+ this.tableDataGQLUpdateInputType,
+ column.name
+ );
+ column.readonly = column.readonly || !typeInformation.isPartOfType;
+ }
+ for (const column of this.columnInfo) {
+ const typeInformation = this.schemaService.getTypeInformation(
+ this.tableDataGQLCreateInputType,
+ column.name
+ );
+ column.requiredForCreation = typeInformation.isRequired;
+ column.acceptedForCreation = typeInformation.isPartOfType;
}
}
@@ -185,20 +218,6 @@ export class BikesComponent {
);
}
- getType(propertyName: string) {
- return this.schemaService.getPropertyTypeFromSchema(
- 'CargoBike',
- propertyName
- );
- }
-
- isReadonly(propertyName: string) {
- return (
- this.columnInfo.find((column) => column.name === propertyName).readonly ||
- !isPartOfGraphQLDoc(propertyName, CargoBikeFieldsMutableFragmentDoc)
- );
- }
-
isStickyColumn(propertyName: string) {
return (
this.columnInfo.find((column) => column.name === propertyName)?.sticky ||
@@ -210,18 +229,50 @@ export class BikesComponent {
return this.loadingRowIds.includes(id);
}
+ validityChange(row: any, columnName: string, isValid: Event) {
+ if (!row.FieldsValidity) {
+ row['FieldsValidity'] = {};
+ }
+ row['FieldsValidity'][columnName] = isValid;
+ }
+
+ countUnvalidFields(row: any) {
+ let unvalidFieldsCount = 0;
+ if (!row.FieldsValidity) {
+ return 99;
+ }
+ for (const prop in row.FieldsValidity) {
+ if (!row.FieldsValidity[prop]) {
+ unvalidFieldsCount++;
+ }
+ }
+ return unvalidFieldsCount;
+ }
+
reloadTable() {
this.reloadingTable = true;
this.bikesService.loadBikes();
}
+ addEmptyRow() {
+ this.data.data = [{ newObject: true }, ...this.data.data];
+ }
+
+ create(row: any) {
+ const newBike = this.schemaService.filterObject(
+ this.tableDataGQLCreateInputType,
+ deepen(row)
+ );
+ this.bikesService.createBike({ bike: newBike });
+ }
+
edit(row: CargoBikeResult) {
this.bikesService.lockBike({ id: row.id });
}
save(row: CargoBikeResult) {
- const deepenRow: CargoBikeUpdateInput = filter(
- CargoBikeFieldsMutableFragmentDoc,
+ const deepenRow = this.schemaService.filterObject(
+ this.tableDataGQLUpdateInputType,
deepen(row)
);
this.bikesService.updateBike({ bike: deepenRow });
diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts
index 6878b02..46aaae0 100644
--- a/src/app/services/auth.service.ts
+++ b/src/app/services/auth.service.ts
@@ -67,10 +67,12 @@ export class AuthService {
)
.pipe(
catchError((error: any) => {
+ console.log(error);
if (error.status === 400) {
this.removeTokens();
this.checkIfUserIsLoggedIn();
location.replace('/login');
+
}
return of();
})
diff --git a/src/app/services/bikes.service.ts b/src/app/services/bikes.service.ts
index 20efacb..1684738 100644
--- a/src/app/services/bikes.service.ts
+++ b/src/app/services/bikes.service.ts
@@ -13,6 +13,8 @@ import {
LockCargoBikeMutationVariables,
UnlockCargoBikeGQL,
UnlockCargoBikeMutationVariables,
+ CreateCargoBikeGQL,
+ CreateCargoBikeMutationVariables,
} from 'src/generated/graphql';
import { DeepExtractTypeSkipArrays } from 'ts-deep-extract-types';
@@ -35,7 +37,8 @@ export class BikesService {
private getCargoBikeByIdGQL: GetCargoBikeByIdGQL,
private updateCargoBikeGQL: UpdateCargoBikeGQL,
private lockCargoBikeGQL: LockCargoBikeGQL,
- private unlockCargoBikeGQL: UnlockCargoBikeGQL
+ private unlockCargoBikeGQL: UnlockCargoBikeGQL,
+ private createCargoBikeGQL: CreateCargoBikeGQL,
) {}
addLoadingRowId(id: string) {
@@ -80,6 +83,18 @@ export class BikesService {
});
}
+ createBike(variables: CreateCargoBikeMutationVariables) {
+ console.log(variables);
+ this.createCargoBikeGQL
+ .mutate(variables)
+ .subscribe((result) => {
+ const newBike = result.data.createCargoBike;
+ this.bikes.next(
+ [newBike, ...this.bikes.value]
+ );
+ })
+ }
+
updateBike(variables: UpdateCargoBikeMutationVariables) {
this.addLoadingRowId(variables.bike.id);
this.updateCargoBikeGQL
diff --git a/src/app/services/schema.service.ts b/src/app/services/schema.service.ts
index 60d44d9..90d383c 100644
--- a/src/app/services/schema.service.ts
+++ b/src/app/services/schema.service.ts
@@ -5,7 +5,6 @@ import jsonSchema from 'src/generated/graphql.schema.json';
providedIn: 'root',
})
export class SchemaService {
-
/** expects startingObject and variablePath and returns its type e.g. cargoBike, security.name -> returns the type of the name variable */
getPropertyTypeFromSchema(
startingObjectName: string,
@@ -21,9 +20,15 @@ export class SchemaService {
);
const type = field.type.name || field.type.ofType.name;
if (variablePath.length === 1) {
- if (field.type.kind === 'ENUM') {
+ if (
+ field.type.kind === 'ENUM' ||
+ (field.type.kind === 'NON_NULL' && field.type.ofType.kind === 'ENUM')
+ ) {
return (
- 'Enum//' + this.getEnumValuesFromSchema(field.type.name).join('//')
+ 'Enum//' +
+ this.getEnumValuesFromSchema(
+ field.type.name || field.type.ofType.name
+ ).join('//')
);
}
return type;
@@ -40,4 +45,70 @@ export class SchemaService {
const type = types.find((type) => type.name === typeName);
return type.enumValues.map((value) => value.name);
}
+
+ /** expects startingObject and variablePath and returns TypeInformation like isPartOfType, type, isRequired e.g. cargoBike, security.name -> returns {isPartOfType: true, type: "string", isRequired: false} */
+ getTypeInformation(
+ startingObjectName: string,
+ variable: string
+ ): { isPartOfType: boolean; type: string; isRequired: boolean } {
+ const variablePath = variable.split('.');
+ const types = jsonSchema.__schema.types;
+ const startingObject = types.find(
+ (type) => type.name === startingObjectName
+ );
+ if (!startingObject) {
+ return { isPartOfType: false, type: '', isRequired: false };
+ }
+ const fields = startingObject.fields || startingObject.inputFields;
+ if (!fields) {
+ return { isPartOfType: false, type: '', isRequired: false };
+ }
+ const field = fields.find((field) => field.name === variablePath[0]);
+ if (!field) {
+ return { isPartOfType: false, type: '', isRequired: false };
+ }
+ const type = field.type.name || field.type.ofType.name;
+ if (variablePath.length === 1) {
+ const isRequired = field.type.kind === 'NON_NULL';
+ if (
+ field.type.kind === 'ENUM' ||
+ (isRequired && field.type.ofType.kind === 'ENUM')
+ ) {
+ return {
+ isPartOfType: true,
+ type:
+ 'Enum//' +
+ this.getEnumValuesFromSchema(
+ field.type.name || field.type.ofType.name
+ ).join('//'),
+ isRequired: isRequired,
+ };
+ } else
+ return {
+ isPartOfType: true,
+ type: type,
+ isRequired: isRequired,
+ };
+ } else {
+ return this.getTypeInformation(type, variablePath.slice(1).join('.'));
+ }
+ }
+
+ filterObject(graphQLTypeName: string, object: object): any {
+ const filteredObject = {};
+ for (const prop in object) {
+ if (typeof object[prop] === 'object' && object[prop] !== null) {
+ const info = this.getTypeInformation(graphQLTypeName, prop);
+ if (info.isPartOfType) {
+ filteredObject[prop] = this.filterObject(info.type, object[prop]);
+ }
+ } else {
+ const info = this.getTypeInformation(graphQLTypeName, prop);
+ if (info.isPartOfType) {
+ filteredObject[prop] = object[prop];
+ }
+ }
+ }
+ return filteredObject;
+ }
}
diff --git a/src/generated/graphql.schema.json b/src/generated/graphql.schema.json
index d623fb0..7da1a2e 100644
--- a/src/generated/graphql.schema.json
+++ b/src/generated/graphql.schema.json
@@ -28,6 +28,16 @@
"enumValues": null,
"possibleTypes": null
},
+ {
+ "kind": "SCALAR",
+ "name": "Money",
+ "description": "is of american format [-]$[0-9]+.[0-9][0-9]\ncommas every three digits and . for decimals with 2 digits after the .\nThere can be a leading -.\nThere is a currency signe at the first position or second position if - is set.\nThe kind of currency depends on the database.",
+ "fields": null,
+ "inputFields": null,
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
{
"kind": "OBJECT",
"name": "CargoBike",
@@ -1115,11 +1125,11 @@
},
{
"name": "projectAllowance",
- "description": "Projektzuschuss",
+ "description": "Projektzuschuss:\nis of american format [-]$[0-9]+.[0-9][0-9]\ncommas every three digits and . for decimals with 2 digits after the .\nThere can be a leading -.\nThere is a currency signe at the first position or second position if - is set.\nThe kind of currency depends on the database.",
"args": [],
"type": {
"kind": "SCALAR",
- "name": "Float",
+ "name": "Money",
"ofType": null
},
"isDeprecated": false,
@@ -1279,10 +1289,10 @@
},
{
"name": "projectAllowance",
- "description": "Projektzuschuss",
+ "description": "Projektzuschuss:\nmust be of format [+|-][$][0-9]*[.[0-9]*]\ncommas are ignored, non numeric values except , and . lead to errors\nThere can be a leading + or -.\nYou can pass a currency signe at the first position or second position of + or - is set.\nThe kind of currency depends on the database.",
"type": {
"kind": "SCALAR",
- "name": "Float",
+ "name": "Money",
"ofType": null
},
"defaultValue": null
@@ -1400,10 +1410,10 @@
},
{
"name": "projectAllowance",
- "description": "Projektzuschuss",
+ "description": "Projektzuschuss:\nmust be of format [+|-][$][0-9]*[.[0-9]*]\ncommas are ignored, non numeric values except , and . lead to errors\nThere can be a leading + or -.\nYou can pass a currency signe at the first position or second position of + or - is set.\nThe kind of currency depends on the database.",
"type": {
"kind": "SCALAR",
- "name": "Float",
+ "name": "Money",
"ofType": null
},
"defaultValue": null
@@ -1446,7 +1456,7 @@
},
{
"name": "lockable",
- "description": null,
+ "description": "cover box can be locked",
"args": [],
"type": {
"kind": "NON_NULL",
diff --git a/src/generated/graphql.ts b/src/generated/graphql.ts
index c976396..c7fe1bc 100644
--- a/src/generated/graphql.ts
+++ b/src/generated/graphql.ts
@@ -14,6 +14,14 @@ export type Scalars = {
Date: any;
/** only time hh-mm-ss */
Time: any;
+ /**
+ * is of american format [-]$[0-9]+.[0-9][0-9]
+ * commas every three digits and . for decimals with 2 digits after the .
+ * There can be a leading -.
+ * There is a currency signe at the first position or second position if - is set.
+ * The kind of currency depends on the database.
+ */
+ Money: any;
/** The `Upload` scalar type represents a file upload. */
Upload: any;
};
@@ -22,6 +30,7 @@ export type Scalars = {
+
/** The CargoBike type is central to the graph. You could call it the root. */
export type CargoBike = {
__typename?: 'CargoBike';
@@ -164,8 +173,15 @@ export type InsuranceData = {
maintenanceAgreement?: Maybe
;
hasFixedRate: Scalars['Boolean'];
fixedRate?: Maybe;
- /** Projektzuschuss */
- projectAllowance?: Maybe;
+ /**
+ * Projektzuschuss:
+ * is of american format [-]$[0-9]+.[0-9][0-9]
+ * commas every three digits and . for decimals with 2 digits after the .
+ * There can be a leading -.
+ * There is a currency signe at the first position or second position if - is set.
+ * The kind of currency depends on the database.
+ */
+ projectAllowance?: Maybe;
notes?: Maybe;
};
@@ -181,8 +197,15 @@ export type InsuranceDataCreateInput = {
maintenanceAgreement?: Maybe;
hasFixedRate: Scalars['Boolean'];
fixedRate?: Maybe;
- /** Projektzuschuss */
- projectAllowance?: Maybe;
+ /**
+ * Projektzuschuss:
+ * must be of format [+|-][$][0-9]*[.[0-9]*]
+ * commas are ignored, non numeric values except , and . lead to errors
+ * There can be a leading + or -.
+ * You can pass a currency signe at the first position or second position of + or - is set.
+ * The kind of currency depends on the database.
+ */
+ projectAllowance?: Maybe;
notes?: Maybe;
};
@@ -198,8 +221,15 @@ export type InsuranceDataUpdateInput = {
maintenanceAgreement?: Maybe;
hasFixedRate?: Maybe;
fixedRate?: Maybe;
- /** Projektzuschuss */
- projectAllowance?: Maybe;
+ /**
+ * Projektzuschuss:
+ * must be of format [+|-][$][0-9]*[.[0-9]*]
+ * commas are ignored, non numeric values except , and . lead to errors
+ * There can be a leading + or -.
+ * You can pass a currency signe at the first position or second position of + or - is set.
+ * The kind of currency depends on the database.
+ */
+ projectAllowance?: Maybe;
notes?: Maybe;
};
@@ -207,6 +237,7 @@ export type InsuranceDataUpdateInput = {
export type DimensionsAndLoad = {
__typename?: 'DimensionsAndLoad';
hasCoverBox: Scalars['Boolean'];
+ /** cover box can be locked */
lockable: Scalars['Boolean'];
boxLength: Scalars['Float'];
boxWidth: Scalars['Float'];
@@ -1681,6 +1712,19 @@ export type GetCargoBikeByIdQuery = (
)> }
);
+export type CreateCargoBikeMutationVariables = Exact<{
+ bike: CargoBikeCreateInput;
+}>;
+
+
+export type CreateCargoBikeMutation = (
+ { __typename?: 'Mutation' }
+ & { createCargoBike: (
+ { __typename?: 'CargoBike' }
+ & CargoBikeFieldsFragment
+ ) }
+);
+
export type UpdateCargoBikeMutationVariables = Exact<{
bike: CargoBikeUpdateInput;
}>;
@@ -1962,6 +2006,24 @@ export const GetCargoBikeByIdDocument = gql`
export class GetCargoBikeByIdGQL extends Apollo.Query {
document = GetCargoBikeByIdDocument;
+ constructor(apollo: Apollo.Apollo) {
+ super(apollo);
+ }
+ }
+export const CreateCargoBikeDocument = gql`
+ mutation CreateCargoBike($bike: CargoBikeCreateInput!) {
+ createCargoBike(cargoBike: $bike) {
+ ...CargoBikeFields
+ }
+}
+ ${CargoBikeFieldsFragmentDoc}`;
+
+ @Injectable({
+ providedIn: 'root'
+ })
+ export class CreateCargoBikeGQL extends Apollo.Mutation {
+ document = CreateCargoBikeDocument;
+
constructor(apollo: Apollo.Apollo) {
super(apollo);
}