From 128f024b22568d996145a27d8e6216f01838e6d3 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Wed, 18 Dec 2024 09:52:28 +0100 Subject: [PATCH 1/6] Cleanup --- .../agent-historical-person-form.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/app/data-entry/agent-form/agent-historical-person-form/agent-historical-person-form.component.ts b/frontend/src/app/data-entry/agent-form/agent-historical-person-form/agent-historical-person-form.component.ts index d2cc83a..4cce239 100644 --- a/frontend/src/app/data-entry/agent-form/agent-historical-person-form/agent-historical-person-form.component.ts +++ b/frontend/src/app/data-entry/agent-form/agent-historical-person-form/agent-historical-person-form.component.ts @@ -7,7 +7,6 @@ import { DataEntryUpdateAgentMutation, } from "generated/graphql"; import { - combineLatest, debounceTime, filter, map, From 78173bf72545658da4ad5bad83460bb61de94676 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Wed, 18 Dec 2024 15:01:31 +0100 Subject: [PATCH 2/6] Add Regions subform --- .../space/mutations/UpdateSpaceMutation.py | 5 +- frontend/generated/graphql.ts | 112 +++++++++++++ frontend/generated/schema.graphql | 3 + .../agent-historical-person-form.component.ts | 7 +- .../location-form.component.html | 2 +- .../location-form/location-form.module.ts | 6 + .../location-regions-form.component.html | 12 ++ .../location-regions-form.component.scss | 0 .../location-regions-form.component.spec.ts | 25 +++ .../location-regions-form.component.ts | 158 ++++++++++++++++++ .../location-regions-form.graphql | 16 ++ .../shared/data-entry-shared.module.ts | 3 + .../multiselect/multiselect.component.html | 16 +- .../multiselect/multiselect.component.ts | 1 + .../subspace-select.component.html | 39 +++++ .../subspace-select.component.scss | 0 .../subspace-select.component.spec.ts | 23 +++ .../subspace-select.component.ts | 85 ++++++++++ frontend/src/assets/icons/church.svg | 1 + frontend/src/assets/icons/crown.svg | 2 + frontend/src/assets/icons/land.svg | 1 + 21 files changed, 508 insertions(+), 9 deletions(-) create mode 100644 frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.html create mode 100644 frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.scss create mode 100644 frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.spec.ts create mode 100644 frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.ts create mode 100644 frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.graphql create mode 100644 frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.html create mode 100644 frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.scss create mode 100644 frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.spec.ts create mode 100644 frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.ts create mode 100644 frontend/src/assets/icons/church.svg create mode 100644 frontend/src/assets/icons/crown.svg create mode 100644 frontend/src/assets/icons/land.svg diff --git a/backend/space/mutations/UpdateSpaceMutation.py b/backend/space/mutations/UpdateSpaceMutation.py index 21247f9..ad8058c 100644 --- a/backend/space/mutations/UpdateSpaceMutation.py +++ b/backend/space/mutations/UpdateSpaceMutation.py @@ -1,8 +1,6 @@ from graphene import ID, Boolean, InputObjectType, List, NonNull, ResolveInfo from django.core.exceptions import ObjectDoesNotExist -from core.types.input.DescriptionFieldInputType import DescriptionFieldInputType from core.types.input.EntityDescriptionInputType import EntityDescriptionInputType -from letter.models import LetterDescription from graphql_app.LettercraftMutation import LettercraftMutation from graphql_app.types.LettercraftErrorType import LettercraftErrorType @@ -11,6 +9,9 @@ class UpdateSpaceInput(EntityDescriptionInputType, InputObjectType): id = ID(required=True) + regions = List(NonNull(ID)) + settlements = List(NonNull(ID)) + structures = List(NonNull(ID)) class UpdateSpaceMutation(LettercraftMutation): diff --git a/frontend/generated/graphql.ts b/frontend/generated/graphql.ts index c9f5219..6e96354 100644 --- a/frontend/generated/graphql.ts +++ b/frontend/generated/graphql.ts @@ -1060,6 +1060,9 @@ export type UpdateSpaceInput = { id: Scalars['ID']['input']; name?: InputMaybe; page?: InputMaybe; + regions?: InputMaybe>; + settlements?: InputMaybe>; + structures?: InputMaybe>; }; export type UpdateSpaceMutation = { @@ -1350,6 +1353,30 @@ export type DataEntryLocationIdentificationQueryVariables = Exact<{ export type DataEntryLocationIdentificationQuery = { __typename?: 'Query', spaceDescription?: { __typename?: 'SpaceDescriptionType', id: string, name: string, description: string } | null }; +export type DataEntryLocationRegionsQueryVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type DataEntryLocationRegionsQuery = { __typename?: 'Query', spaceDescription?: { __typename?: 'SpaceDescriptionType', id: string, regions: Array<{ __typename?: 'RegionType', id: string }> } | null }; + +export type DataEntryRegionsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type DataEntryRegionsQuery = { __typename?: 'Query', regions: Array<{ __typename?: 'RegionType', id: string, name: string, type: SpaceRegionTypeChoices }> }; + +export type DataEntryLocationSettlementsQueryVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type DataEntryLocationSettlementsQuery = { __typename?: 'Query', spaceDescription?: { __typename?: 'SpaceDescriptionType', id: string, settlements: Array<{ __typename?: 'SettlementType', id: string }> } | null }; + +export type DataEntrySettlementsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type DataEntrySettlementsQuery = { __typename?: 'Query', settlements: Array<{ __typename?: 'SettlementType', id: string, name: string, regions: Array<{ __typename?: 'RegionType', id: string, name: string }> }> }; + export type DataEntryLocationSourceTextQueryVariables = Exact<{ id: Scalars['ID']['input']; }>; @@ -2427,6 +2454,91 @@ export const DataEntryLocationIdentificationDocument = gql` export class DataEntryLocationIdentificationGQL extends Apollo.Query { override document = DataEntryLocationIdentificationDocument; + constructor(apollo: Apollo.Apollo) { + super(apollo); + } + } +export const DataEntryLocationRegionsDocument = gql` + query DataEntryLocationRegions($id: ID!) { + spaceDescription(id: $id) { + id + regions { + id + } + } +} + `; + + @Injectable({ + providedIn: 'root' + }) + export class DataEntryLocationRegionsGQL extends Apollo.Query { + override document = DataEntryLocationRegionsDocument; + + constructor(apollo: Apollo.Apollo) { + super(apollo); + } + } +export const DataEntryRegionsDocument = gql` + query DataEntryRegions { + regions { + id + name + type + } +} + `; + + @Injectable({ + providedIn: 'root' + }) + export class DataEntryRegionsGQL extends Apollo.Query { + override document = DataEntryRegionsDocument; + + constructor(apollo: Apollo.Apollo) { + super(apollo); + } + } +export const DataEntryLocationSettlementsDocument = gql` + query DataEntryLocationSettlements($id: ID!) { + spaceDescription(id: $id) { + id + settlements { + id + } + } +} + `; + + @Injectable({ + providedIn: 'root' + }) + export class DataEntryLocationSettlementsGQL extends Apollo.Query { + override document = DataEntryLocationSettlementsDocument; + + constructor(apollo: Apollo.Apollo) { + super(apollo); + } + } +export const DataEntrySettlementsDocument = gql` + query DataEntrySettlements { + settlements { + id + name + regions { + id + name + } + } +} + `; + + @Injectable({ + providedIn: 'root' + }) + export class DataEntrySettlementsGQL extends Apollo.Query { + override document = DataEntrySettlementsDocument; + constructor(apollo: Apollo.Apollo) { super(apollo); } diff --git a/frontend/generated/schema.graphql b/frontend/generated/schema.graphql index c436e9d..d4a53ae 100644 --- a/frontend/generated/schema.graphql +++ b/frontend/generated/schema.graphql @@ -905,6 +905,9 @@ input UpdateSpaceInput { id: ID! name: String page: String + regions: [ID!] + settlements: [ID!] + structures: [ID!] } type UpdateSpaceMutation { diff --git a/frontend/src/app/data-entry/agent-form/agent-historical-person-form/agent-historical-person-form.component.ts b/frontend/src/app/data-entry/agent-form/agent-historical-person-form/agent-historical-person-form.component.ts index 4cce239..5dfd35b 100644 --- a/frontend/src/app/data-entry/agent-form/agent-historical-person-form/agent-historical-person-form.component.ts +++ b/frontend/src/app/data-entry/agent-form/agent-historical-person-form/agent-historical-person-form.component.ts @@ -44,12 +44,9 @@ export class AgentHistoricalPersonFormComponent implements OnInit, OnDestroy { share() ); - private allHistoricalPersons$ = this.historicalPersonsQuery - .fetch() - .pipe(map((result) => result.data.historicalPersons)); - public historicalPersonOptions$: Observable = - this.allHistoricalPersons$.pipe( + this.historicalPersonsQuery.fetch().pipe( + map((result) => result.data.historicalPersons), map((persons) => persons.map(({ id, name }) => ({ value: id, diff --git a/frontend/src/app/data-entry/location-form/location-form.component.html b/frontend/src/app/data-entry/location-form/location-form.component.html index fb7d804..e43c957 100644 --- a/frontend/src/app/data-entry/location-form/location-form.component.html +++ b/frontend/src/app/data-entry/location-form/location-form.component.html @@ -32,7 +32,7 @@

Source text

Regions

-

Coming soon!

+

Settlements

Coming soon!

diff --git a/frontend/src/app/data-entry/location-form/location-form.module.ts b/frontend/src/app/data-entry/location-form/location-form.module.ts index 4279a28..57dddbd 100644 --- a/frontend/src/app/data-entry/location-form/location-form.module.ts +++ b/frontend/src/app/data-entry/location-form/location-form.module.ts @@ -5,6 +5,9 @@ import { SharedModule } from "@shared/shared.module"; import { LocationIdentificationFormComponent } from "./location-identification-form/location-identification-form.component"; import { LocationSourceTextFormComponent } from "./location-source-text-form/location-source-text-form.component"; import { LocationEpisodesFormComponent } from './location-episodes-form/location-episodes-form.component'; +import { LocationRegionsFormComponent } from './location-regions-form/location-regions-form.component'; +import { LocationSettlementsFormComponent } from './location-settlements-form/location-settlements-form.component'; +import { LocationStructuresFormComponent } from './location-structures-form/location-structures-form.component'; @@ -14,6 +17,9 @@ import { LocationEpisodesFormComponent } from './location-episodes-form/location LocationIdentificationFormComponent, LocationEpisodesFormComponent, LocationSourceTextFormComponent, + LocationRegionsFormComponent, + LocationSettlementsFormComponent, + LocationStructuresFormComponent, ], imports: [SharedModule, DataEntrySharedModule], exports: [LocationFormComponent], diff --git a/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.html b/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.html new file mode 100644 index 0000000..e589efb --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.html @@ -0,0 +1,12 @@ +
+

+ Pick one or more regions that contain the location represented here. +

+ + + diff --git a/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.scss b/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.spec.ts b/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.spec.ts new file mode 100644 index 0000000..bc632ba --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { LocationRegionsFormComponent } from "./location-regions-form.component"; +import { SharedTestingModule } from "@shared/shared-testing.module"; +import { FormService } from "../../shared/form.service"; + +describe("LocationRegionsFormComponent", () => { + let component: LocationRegionsFormComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [LocationRegionsFormComponent], + imports: [SharedTestingModule], + providers: [FormService], + }); + fixture = TestBed.createComponent(LocationRegionsFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.ts b/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.ts new file mode 100644 index 0000000..977d4f0 --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.component.ts @@ -0,0 +1,158 @@ +import { Component, DestroyRef, OnDestroy, OnInit } from "@angular/core"; +import { ToastService } from "@services/toast.service"; +import { FormService } from "../../shared/form.service"; +import { + debounceTime, + filter, + map, + Observable, + share, + switchMap, + withLatestFrom, +} from "rxjs"; +import { + DataEntryLocationRegionsGQL, + DataEntryRegionsGQL, + DataEntryUpdateLocationGQL, + DataEntryUpdateLocationMutation, + DataEntryUpdateLocationMutationVariables, + SpaceRegionTypeChoices, +} from "generated/graphql"; +import { FormControl, FormGroup } from "@angular/forms"; +import { formStatusSubject } from "../../shared/utils"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { MutationResult } from "apollo-angular"; +import { MultiselectOption } from "../../shared/multiselect/multiselect.component"; + +type LocationRegionsSpaceDescription = Required< + Pick +>; + +type LocationRegionsForm = { + [K in keyof LocationRegionsSpaceDescription]: FormControl< + LocationRegionsSpaceDescription[K] + >; +}; + +const regionTypeIconMapping: Record = { + [SpaceRegionTypeChoices.Ecclesiastical]: "church", + [SpaceRegionTypeChoices.Geographical]: "land", + [SpaceRegionTypeChoices.Political]: "crown", +}; + +@Component({ + selector: "lc-location-regions-form", + templateUrl: "./location-regions-form.component.html", + styleUrls: ["./location-regions-form.component.scss"], +}) +export class LocationRegionsFormComponent implements OnInit, OnDestroy { + private id$ = this.formService.id$; + + private location$ = this.id$.pipe( + switchMap((id) => this.locationQuery.watch({ id }).valueChanges), + map((result) => result.data.spaceDescription), + share() + ); + + public regionOptions$: Observable = this.regionsQuery + .fetch() + .pipe( + map((result) => result.data.regions), + map((regions) => + regions.map(({ id, name, type }) => ({ + value: id, + label: name, + icon: regionTypeIconMapping[type], + })) + ) + ); + + public form = new FormGroup({ + regions: new FormControl([], { + nonNullable: true, + }), + }); + + private formName = "regions"; + private status$ = formStatusSubject(); + + constructor( + private destroyRef: DestroyRef, + private formService: FormService, + private toastService: ToastService, + private locationQuery: DataEntryLocationRegionsGQL, + private regionsQuery: DataEntryRegionsGQL, + private updateLocation: DataEntryUpdateLocationGQL + ) {} + + ngOnInit(): void { + this.formService.attachForm(this.formName, this.status$); + + this.location$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((location) => { + const regionIds = location?.regions.map((region) => region.id); + this.form.patchValue({ regions: regionIds }); + }); + + this.form.statusChanges + .pipe( + filter((status) => status === "INVALID"), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe(() => this.status$.next("invalid")); + + const validFormSubmission$ = this.location$.pipe( + switchMap(() => + this.form.valueChanges.pipe( + map(() => this.form.getRawValue()), + filter(() => this.form.valid) + ) + ), + debounceTime(300), + share() + ); + validFormSubmission$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.status$.next("loading")); + + validFormSubmission$ + .pipe( + withLatestFrom(this.id$), + switchMap(([agent, id]) => this.performMutation(agent, id)), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe((result) => this.onMutationResult(result)); + } + + ngOnDestroy(): void { + this.formService.detachForm(this.formName); + } + + private performMutation( + form: LocationRegionsSpaceDescription, + id: string + ): Observable> { + return this.updateLocation.mutate({ + spaceData: { + id, + regions: form.regions, + }, + }); + } + + private onMutationResult( + result: MutationResult + ): void { + const errors = result.data?.updateSpace?.errors; + if (errors && errors.length > 0) { + this.status$.next("error"); + this.toastService.show({ + body: errors.map((error) => error.messages).join("\n"), + type: "danger", + header: "Update failed", + }); + } + this.status$.next("saved"); + } +} diff --git a/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.graphql b/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.graphql new file mode 100644 index 0000000..2166527 --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-regions-form/location-regions-form.graphql @@ -0,0 +1,16 @@ +query DataEntryLocationRegions($id: ID!) { + spaceDescription(id: $id) { + id + regions { + id + } + } +} + +query DataEntryRegions { + regions { + id + name + type + } +} diff --git a/frontend/src/app/data-entry/shared/data-entry-shared.module.ts b/frontend/src/app/data-entry/shared/data-entry-shared.module.ts index 5cd6fcb..22afcf8 100644 --- a/frontend/src/app/data-entry/shared/data-entry-shared.module.ts +++ b/frontend/src/app/data-entry/shared/data-entry-shared.module.ts @@ -8,6 +8,7 @@ import { DeleteEntityButtonComponent } from "./delete-entity-button/delete-entit import { FormStatusComponent } from "./form-status/form-status.component"; import { EpisodeLinkFormComponent } from "./episode-link-form/episode-link-form.component"; import { HistoricalPersonSelectComponent } from "./historical-person-select/historical-person-select.component"; +import { SubspaceSelectComponent } from "./subspace-select/subspace-select.component"; @NgModule({ declarations: [ @@ -19,6 +20,7 @@ import { HistoricalPersonSelectComponent } from "./historical-person-select/hist FormStatusComponent, EpisodeLinkFormComponent, HistoricalPersonSelectComponent, + SubspaceSelectComponent, ], imports: [SharedModule], exports: [ @@ -30,6 +32,7 @@ import { HistoricalPersonSelectComponent } from "./historical-person-select/hist FormStatusComponent, EpisodeLinkFormComponent, HistoricalPersonSelectComponent, + SubspaceSelectComponent, ], }) export class DataEntrySharedModule {} diff --git a/frontend/src/app/data-entry/shared/multiselect/multiselect.component.html b/frontend/src/app/data-entry/shared/multiselect/multiselect.component.html index f8bd9a2..19daf0a 100644 --- a/frontend/src/app/data-entry/shared/multiselect/multiselect.component.html +++ b/frontend/src/app/data-entry/shared/multiselect/multiselect.component.html @@ -17,9 +17,23 @@ " (click)="selectOption(option)" [disabled]="selectedOptions().includes(option.value)" + class="d-inline-flex align-items-center" ngbDropdownItem > - {{ option.label }} + + {{ option.dropdownLabel ?? option.label }} diff --git a/frontend/src/app/data-entry/shared/multiselect/multiselect.component.ts b/frontend/src/app/data-entry/shared/multiselect/multiselect.component.ts index 7fdf12c..89e5b78 100644 --- a/frontend/src/app/data-entry/shared/multiselect/multiselect.component.ts +++ b/frontend/src/app/data-entry/shared/multiselect/multiselect.component.ts @@ -12,6 +12,7 @@ import { NgbDropdownToggle } from "@ng-bootstrap/ng-bootstrap"; export interface MultiselectOption { value: string; label: string; + icon?: string; } @Component({ diff --git a/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.html b/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.html new file mode 100644 index 0000000..ed9fb69 --- /dev/null +++ b/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.html @@ -0,0 +1,39 @@ +

Currently selected

+
    +
  • + + {{ subSpace.label }} + +
  • +
+ +
+ + diff --git a/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.scss b/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.spec.ts b/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.spec.ts new file mode 100644 index 0000000..921d205 --- /dev/null +++ b/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { SubspaceSelectComponent } from "./subspace-select.component"; +import { SharedTestingModule } from "@shared/shared-testing.module"; + +describe("SubspaceSelectComponent", () => { + let component: SubspaceSelectComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [SubspaceSelectComponent], + imports: [SharedTestingModule] + }); + fixture = TestBed.createComponent(SubspaceSelectComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.ts b/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.ts new file mode 100644 index 0000000..0f6d208 --- /dev/null +++ b/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.ts @@ -0,0 +1,85 @@ +import { + Component, + computed, + DestroyRef, + forwardRef, + Input, + OnInit, +} from "@angular/core"; +import { + ControlValueAccessor, + FormControl, + NG_VALUE_ACCESSOR, +} from "@angular/forms"; +import { MultiselectOption } from "../multiselect/multiselect.component"; +import { actionIcons } from "@shared/icons"; +import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop"; + +@Component({ + selector: "lc-subspace-select", + templateUrl: "./subspace-select.component.html", + styleUrls: ["./subspace-select.component.scss"], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SubspaceSelectComponent), + multi: true, + }, + ], +}) +export class SubspaceSelectComponent implements ControlValueAccessor, OnInit { + @Input({ required: true }) subspaceType: 'region' | 'settlement' | 'structure' = 'region'; + @Input({ required: true }) options: MultiselectOption[] = []; + + public control = new FormControl([], { nonNullable: true }); + public actionIcons = actionIcons; + + private onChange: ((value: string[]) => void) | null = null; + private onTouched: (() => void) | null = null; + + public formValue = toSignal(this.control.valueChanges); + public selectedSubspaces = computed(() => { + const selectedIds = this.formValue(); + return this.options.filter((item) => selectedIds?.includes(item.value)); + }); + + constructor(private destroyRef: DestroyRef) {} + + ngOnInit(): void { + this.control.valueChanges + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((value) => { + if (this.onTouched) { + this.onTouched(); + } + if (this.onChange) { + this.onChange(value); + } + }); + } + + public removeSubspace(id: string): void { + const value = this.control.value.filter((item) => item !== id); + this.control.setValue(value); + } + + public writeValue(obj: string[]): void { + this.control.setValue(obj); + } + + public registerOnChange(fn: (value: string[] | null) => void): void { + this.onChange = fn; + } + + public registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + public setDisabledState?(isDisabled: boolean): void { + if (isDisabled) { + this.control.enable(); + } else { + this.control.disable(); + } + } +} diff --git a/frontend/src/assets/icons/church.svg b/frontend/src/assets/icons/church.svg new file mode 100644 index 0000000..f4cef2c --- /dev/null +++ b/frontend/src/assets/icons/church.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/icons/crown.svg b/frontend/src/assets/icons/crown.svg new file mode 100644 index 0000000..677224a --- /dev/null +++ b/frontend/src/assets/icons/crown.svg @@ -0,0 +1,2 @@ + + diff --git a/frontend/src/assets/icons/land.svg b/frontend/src/assets/icons/land.svg new file mode 100644 index 0000000..2c02391 --- /dev/null +++ b/frontend/src/assets/icons/land.svg @@ -0,0 +1 @@ + From 63ab63dedcea9a7c5d7b08f6fd009562c962fcbc Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Wed, 18 Dec 2024 15:49:13 +0100 Subject: [PATCH 3/6] Add Settlements form --- .../location-form.component.html | 4 +- .../location-settlements-form.component.html | 12 ++ .../location-settlements-form.component.scss | 0 ...ocation-settlements-form.component.spec.ts | 25 +++ .../location-settlements-form.component.ts | 170 ++++++++++++++++++ .../location-settlements-form.graphql | 19 ++ .../multiselect/multiselect.component.ts | 2 + 7 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.html create mode 100644 frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.scss create mode 100644 frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.spec.ts create mode 100644 frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.ts create mode 100644 frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.graphql diff --git a/frontend/src/app/data-entry/location-form/location-form.component.html b/frontend/src/app/data-entry/location-form/location-form.component.html index e43c957..d46946b 100644 --- a/frontend/src/app/data-entry/location-form/location-form.component.html +++ b/frontend/src/app/data-entry/location-form/location-form.component.html @@ -35,10 +35,10 @@

Regions

Settlements

-

Coming soon!

+

Structures

-

Coming soon!

+

Episodes

diff --git a/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.html b/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.html new file mode 100644 index 0000000..840fbe6 --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.html @@ -0,0 +1,12 @@ +
+

+ Pick one or more settlements that contain the location represented here. +

+ + + diff --git a/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.scss b/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.spec.ts b/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.spec.ts new file mode 100644 index 0000000..d0226bd --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { LocationSettlementsFormComponent } from "./location-settlements-form.component"; +import { SharedTestingModule } from "@shared/shared-testing.module"; +import { FormService } from "../../shared/form.service"; + +describe("LocationSettlementsFormComponent", () => { + let component: LocationSettlementsFormComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [LocationSettlementsFormComponent], + imports: [SharedTestingModule], + providers: [FormService], + }); + fixture = TestBed.createComponent(LocationSettlementsFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.ts b/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.ts new file mode 100644 index 0000000..0269311 --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.component.ts @@ -0,0 +1,170 @@ +import { Component, DestroyRef, OnDestroy, OnInit } from "@angular/core"; +import { + DataEntryLocationSettlementsGQL, + DataEntrySettlementsGQL, + DataEntrySettlementsQuery, + DataEntryUpdateLocationGQL, + DataEntryUpdateLocationMutation, + DataEntryUpdateLocationMutationVariables, +} from "generated/graphql"; +import { FormService } from "../../shared/form.service"; +import { ToastService } from "@services/toast.service"; +import { + debounceTime, + filter, + map, + Observable, + share, + switchMap, + withLatestFrom, +} from "rxjs"; +import { FormControl, FormGroup } from "@angular/forms"; +import { formStatusSubject } from "../../shared/utils"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { MutationResult } from "apollo-angular"; + +type LocationSettlementsSpaceDescription = Required< + Pick +>; + +type LocationSettlementsForm = { + [K in keyof LocationSettlementsSpaceDescription]: FormControl< + LocationSettlementsSpaceDescription[K] + >; +}; + +type QueriedSettlement = NonNullable< + DataEntrySettlementsQuery["settlements"][number] +>; + +@Component({ + selector: "lc-location-settlements-form", + templateUrl: "./location-settlements-form.component.html", + styleUrls: ["./location-settlements-form.component.scss"], +}) +export class LocationSettlementsFormComponent implements OnInit, OnDestroy { + private id$ = this.formService.id$; + + private location$ = this.id$.pipe( + switchMap((id) => this.locationQuery.watch({ id }).valueChanges), + map((result) => result.data.spaceDescription), + share() + ); + + public settlementOptions$ = this.settlementsQuery.fetch().pipe( + map((result) => result.data.settlements), + map((settlements) => + settlements.map((settlement) => ({ + value: settlement.id, + label: settlement.name, + dropdownLabel: this.formatDropdownLabel(settlement), + })) + ) + ); + + public form = new FormGroup({ + settlements: new FormControl([], { + nonNullable: true, + }), + }); + + private formName = "settlements"; + private status$ = formStatusSubject(); + + private formatDropdownLabel(settlement: QueriedSettlement): string { + const { name, regions } = settlement; + let label = name; + if (regions.length === 0) { + return label; + } else if (regions.length > 2) { + return (label += ` (part of ${regions[0].name} and ${ + regions.length - 1 + } more)`); + } + return (label += ` (part of ${regions + .map((region) => region.name) + .join(", ")})`); + } + + constructor( + private destroyRef: DestroyRef, + private formService: FormService, + private toastService: ToastService, + private locationQuery: DataEntryLocationSettlementsGQL, + private settlementsQuery: DataEntrySettlementsGQL, + private updateLocation: DataEntryUpdateLocationGQL + ) {} + + ngOnInit(): void { + this.formService.attachForm(this.formName, this.status$); + + this.location$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((location) => { + const settlementIds = location?.settlements.map( + (settlement) => settlement.id + ); + this.form.patchValue({ settlements: settlementIds }); + }); + + this.form.statusChanges + .pipe( + filter((status) => status === "INVALID"), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe(() => this.status$.next("invalid")); + + const validFormSubmission$ = this.location$.pipe( + switchMap(() => + this.form.valueChanges.pipe( + map(() => this.form.getRawValue()), + filter(() => this.form.valid) + ) + ), + debounceTime(300), + share() + ); + validFormSubmission$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.status$.next("loading")); + + validFormSubmission$ + .pipe( + withLatestFrom(this.id$), + switchMap(([agent, id]) => this.performMutation(agent, id)), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe((result) => this.onMutationResult(result)); + } + + ngOnDestroy(): void { + this.formService.detachForm(this.formName); + } + + private performMutation( + form: LocationSettlementsSpaceDescription, + id: string + ): Observable> { + return this.updateLocation.mutate({ + spaceData: { + id, + settlements: form.settlements, + }, + }); + } + + private onMutationResult( + result: MutationResult + ): void { + const errors = result.data?.updateSpace?.errors; + if (errors && errors.length > 0) { + this.status$.next("error"); + this.toastService.show({ + body: errors.map((error) => error.messages).join("\n"), + type: "danger", + header: "Update failed", + }); + } + this.status$.next("saved"); + } +} diff --git a/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.graphql b/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.graphql new file mode 100644 index 0000000..11325e5 --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-settlements-form/location-settlements-form.graphql @@ -0,0 +1,19 @@ +query DataEntryLocationSettlements($id: ID!) { + spaceDescription(id: $id) { + id + settlements { + id + } + } +} + +query DataEntrySettlements { + settlements { + id + name + regions { + id + name + } + } +} diff --git a/frontend/src/app/data-entry/shared/multiselect/multiselect.component.ts b/frontend/src/app/data-entry/shared/multiselect/multiselect.component.ts index 89e5b78..7c9b9a6 100644 --- a/frontend/src/app/data-entry/shared/multiselect/multiselect.component.ts +++ b/frontend/src/app/data-entry/shared/multiselect/multiselect.component.ts @@ -13,6 +13,8 @@ export interface MultiselectOption { value: string; label: string; icon?: string; + // The label to be shown in the dropdown. If not supplied, `label` will be used. + dropdownLabel?: string; } @Component({ From b115871272fc6d022ce1fda402618c7a56d4d408 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Wed, 18 Dec 2024 15:49:33 +0100 Subject: [PATCH 4/6] Added structures form --- .../location-structures-form.component.html | 12 ++ .../location-structures-form.component.scss | 0 ...location-structures-form.component.spec.ts | 25 +++ .../location-structures-form.component.ts | 172 ++++++++++++++++++ .../location-structures-form.graphql | 20 ++ .../subspace-select.component.html | 2 +- frontend/src/assets/icons/fort.svg | 1 + frontend/src/assets/icons/house.svg | 1 + frontend/src/assets/icons/road.svg | 1 + frontend/src/assets/icons/room.svg | 1 + frontend/src/assets/icons/spot.svg | 1 + 11 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.html create mode 100644 frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.scss create mode 100644 frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.spec.ts create mode 100644 frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.ts create mode 100644 frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.graphql create mode 100644 frontend/src/assets/icons/fort.svg create mode 100644 frontend/src/assets/icons/house.svg create mode 100644 frontend/src/assets/icons/road.svg create mode 100644 frontend/src/assets/icons/room.svg create mode 100644 frontend/src/assets/icons/spot.svg diff --git a/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.html b/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.html new file mode 100644 index 0000000..0082ad3 --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.html @@ -0,0 +1,12 @@ +
+

+ Pick one or more structures that contain the location represented here. +

+ + + diff --git a/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.scss b/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.spec.ts b/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.spec.ts new file mode 100644 index 0000000..22c6c8b --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { LocationStructuresFormComponent } from "./location-structures-form.component"; +import { SharedTestingModule } from "@shared/shared-testing.module"; +import { FormService } from "../../shared/form.service"; + +describe("LocationStructuresFormComponent", () => { + let component: LocationStructuresFormComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [LocationStructuresFormComponent], + imports: [SharedTestingModule], + providers: [FormService], + }); + fixture = TestBed.createComponent(LocationStructuresFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.ts b/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.ts new file mode 100644 index 0000000..8f05f7e --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.component.ts @@ -0,0 +1,172 @@ +import { Component, DestroyRef, OnDestroy, OnInit } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { FormGroup, FormControl } from "@angular/forms"; +import { ToastService } from "@services/toast.service"; +import { MutationResult } from "apollo-angular"; +import { + DataEntryLocationStructuresGQL, + DataEntryStructuresGQL, + DataEntryStructuresQuery, + DataEntryUpdateLocationGQL, + DataEntryUpdateLocationMutation, + DataEntryUpdateLocationMutationVariables, + SpaceStructureLevelChoices, +} from "generated/graphql"; +import { + switchMap, + map, + share, + filter, + debounceTime, + withLatestFrom, + Observable, +} from "rxjs"; +import { FormService } from "../../shared/form.service"; +import { formStatusSubject } from "../../shared/utils"; + +type LocationStructuresSpaceDescription = Required< + Pick +>; + +type LocationStructuresForm = { + [K in keyof LocationStructuresSpaceDescription]: FormControl< + LocationStructuresSpaceDescription[K] + >; +}; + +type QueriedStructure = NonNullable< + DataEntryStructuresQuery["structures"][number] +>; + +const structureTypeIconMapping: Record = { + [SpaceStructureLevelChoices.A_1]: "road", + [SpaceStructureLevelChoices.A_2]: "fort", + [SpaceStructureLevelChoices.A_3]: "house", + [SpaceStructureLevelChoices.A_4]: "room", + [SpaceStructureLevelChoices.A_5]: "spot", +}; + +@Component({ + selector: "lc-location-structures-form", + templateUrl: "./location-structures-form.component.html", + styleUrls: ["./location-structures-form.component.scss"], +}) +export class LocationStructuresFormComponent implements OnInit, OnDestroy { + private id$ = this.formService.id$; + + private location$ = this.id$.pipe( + switchMap((id) => this.locationQuery.watch({ id }).valueChanges), + map((result) => result.data.spaceDescription), + share() + ); + + public structureOptions$ = this.structuresQuery.fetch().pipe( + map((result) => result.data.structures), + map((structures) => + structures.map((structure) => ({ + value: structure.id, + label: structure.name, + icon: structureTypeIconMapping[structure.level], + dropdownLabel: this.formatDropdownLabel(structure), + })) + ) + ); + + public form = new FormGroup({ + structures: new FormControl([], { + nonNullable: true, + }), + }); + + private formName = "structures"; + private status$ = formStatusSubject(); + + private formatDropdownLabel(structure: QueriedStructure): string { + if (!structure.settlement) { + return structure.name; + } + return `${structure.name} (part of ${structure.settlement.name})`; + } + + constructor( + private destroyRef: DestroyRef, + private formService: FormService, + private toastService: ToastService, + private locationQuery: DataEntryLocationStructuresGQL, + private structuresQuery: DataEntryStructuresGQL, + private updateLocation: DataEntryUpdateLocationGQL + ) {} + + ngOnInit(): void { + this.formService.attachForm(this.formName, this.status$); + + this.location$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((location) => { + const structureIds = location?.structures.map( + (structure) => structure.id + ); + this.form.patchValue({ structures: structureIds }); + }); + + this.form.statusChanges + .pipe( + filter((status) => status === "INVALID"), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe(() => this.status$.next("invalid")); + + const validFormSubmission$ = this.location$.pipe( + switchMap(() => + this.form.valueChanges.pipe( + map(() => this.form.getRawValue()), + filter(() => this.form.valid) + ) + ), + debounceTime(300), + share() + ); + validFormSubmission$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.status$.next("loading")); + + validFormSubmission$ + .pipe( + withLatestFrom(this.id$), + switchMap(([agent, id]) => this.performMutation(agent, id)), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe((result) => this.onMutationResult(result)); + } + + ngOnDestroy(): void { + this.formService.detachForm(this.formName); + } + + private performMutation( + form: LocationStructuresSpaceDescription, + id: string + ): Observable> { + return this.updateLocation.mutate({ + spaceData: { + id, + structures: form.structures, + }, + }); + } + + private onMutationResult( + result: MutationResult + ): void { + const errors = result.data?.updateSpace?.errors; + if (errors && errors.length > 0) { + this.status$.next("error"); + this.toastService.show({ + body: errors.map((error) => error.messages).join("\n"), + type: "danger", + header: "Update failed", + }); + } + this.status$.next("saved"); + } +} diff --git a/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.graphql b/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.graphql new file mode 100644 index 0000000..52e569c --- /dev/null +++ b/frontend/src/app/data-entry/location-form/location-structures-form/location-structures-form.graphql @@ -0,0 +1,20 @@ +query DataEntryLocationStructures($id: ID!) { + spaceDescription(id: $id) { + id + structures { + id + } + } +} + +query DataEntryStructures { + structures { + id + name + level + settlement { + id + name + } + } +} diff --git a/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.html b/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.html index ed9fb69..eb53465 100644 --- a/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.html +++ b/frontend/src/app/data-entry/shared/subspace-select/subspace-select.component.html @@ -1,4 +1,4 @@ -

Currently selected

+

Currently selected:

  • diff --git a/frontend/src/assets/icons/house.svg b/frontend/src/assets/icons/house.svg new file mode 100644 index 0000000..e43b5b8 --- /dev/null +++ b/frontend/src/assets/icons/house.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/icons/road.svg b/frontend/src/assets/icons/road.svg new file mode 100644 index 0000000..67df46e --- /dev/null +++ b/frontend/src/assets/icons/road.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/icons/room.svg b/frontend/src/assets/icons/room.svg new file mode 100644 index 0000000..e87efcd --- /dev/null +++ b/frontend/src/assets/icons/room.svg @@ -0,0 +1 @@ + diff --git a/frontend/src/assets/icons/spot.svg b/frontend/src/assets/icons/spot.svg new file mode 100644 index 0000000..421a6a0 --- /dev/null +++ b/frontend/src/assets/icons/spot.svg @@ -0,0 +1 @@ + From 497f1e6b656dc92618697104800b4d92fe6497e0 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Wed, 18 Dec 2024 15:49:48 +0100 Subject: [PATCH 5/6] Avoid unwanted duplication --- backend/event/types/EpisodeType.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/event/types/EpisodeType.py b/backend/event/types/EpisodeType.py index 03078fa..64aea90 100644 --- a/backend/event/types/EpisodeType.py +++ b/backend/event/types/EpisodeType.py @@ -6,6 +6,7 @@ from event.models import Episode, EpisodeCategory from event.types.EpisodeCategoryType import EpisodeCategoryType from person.models import AgentDescription +from space.models import SpaceDescription class EpisodeType(EntityDescriptionType, DjangoObjectType): @@ -13,6 +14,7 @@ class EpisodeType(EntityDescriptionType, DjangoObjectType): agents = List( NonNull("person.types.AgentDescriptionType.AgentDescriptionType"), required=True ) + spaces = List(NonNull("space.types.SpaceDescriptionType.SpaceDescriptionType"), required=True) class Meta: model = Episode @@ -43,6 +45,12 @@ def resolve_agents( # to that agent, for some reason. return parent.agents.distinct() + @staticmethod + def resolve_spaces( + parent: Episode, info: ResolveInfo + ) -> QuerySet[SpaceDescription]: + return parent.spaces.distinct() + @staticmethod def resolve_categories( parent: Episode, info: ResolveInfo From 2d99fba93bc4890bfa32c630daa3e0990aee42d6 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Wed, 18 Dec 2024 15:49:59 +0100 Subject: [PATCH 6/6] Generated files --- frontend/generated/graphql.ts | 58 ++++++++++++++++++++++++++++++- frontend/generated/schema.graphql | 2 -- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/frontend/generated/graphql.ts b/frontend/generated/graphql.ts index 6e96354..7d639f2 100644 --- a/frontend/generated/graphql.ts +++ b/frontend/generated/graphql.ts @@ -284,7 +284,6 @@ export type EpisodeType = EntityDescription & { source: SourceType; /** How is this entity presented in the text? */ sourceMention?: Maybe; - /** locations involved in this episode */ spaces: Array; /** full description of the events in the passage */ summary: Scalars['String']['output']; @@ -1384,6 +1383,18 @@ export type DataEntryLocationSourceTextQueryVariables = Exact<{ export type DataEntryLocationSourceTextQuery = { __typename?: 'Query', spaceDescription?: { __typename?: 'SpaceDescriptionType', id: string, book: string, chapter: string, page: string } | null }; +export type DataEntryLocationStructuresQueryVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type DataEntryLocationStructuresQuery = { __typename?: 'Query', spaceDescription?: { __typename?: 'SpaceDescriptionType', id: string, structures: Array<{ __typename?: 'StructureType', id: string }> } | null }; + +export type DataEntryStructuresQueryVariables = Exact<{ [key: string]: never; }>; + + +export type DataEntryStructuresQuery = { __typename?: 'Query', structures: Array<{ __typename?: 'StructureType', id: string, name: string, level: SpaceStructureLevelChoices, settlement?: { __typename?: 'SettlementType', id: string, name: string } | null }> }; + export type DataEntryLocationQueryVariables = Exact<{ id: Scalars['ID']['input']; }>; @@ -2560,6 +2571,51 @@ export const DataEntryLocationSourceTextDocument = gql` export class DataEntryLocationSourceTextGQL extends Apollo.Query { override document = DataEntryLocationSourceTextDocument; + constructor(apollo: Apollo.Apollo) { + super(apollo); + } + } +export const DataEntryLocationStructuresDocument = gql` + query DataEntryLocationStructures($id: ID!) { + spaceDescription(id: $id) { + id + structures { + id + } + } +} + `; + + @Injectable({ + providedIn: 'root' + }) + export class DataEntryLocationStructuresGQL extends Apollo.Query { + override document = DataEntryLocationStructuresDocument; + + constructor(apollo: Apollo.Apollo) { + super(apollo); + } + } +export const DataEntryStructuresDocument = gql` + query DataEntryStructures { + structures { + id + name + level + settlement { + id + name + } + } +} + `; + + @Injectable({ + providedIn: 'root' + }) + export class DataEntryStructuresGQL extends Apollo.Query { + override document = DataEntryStructuresDocument; + constructor(apollo: Apollo.Apollo) { super(apollo); } diff --git a/frontend/generated/schema.graphql b/frontend/generated/schema.graphql index d4a53ae..8de57ad 100644 --- a/frontend/generated/schema.graphql +++ b/frontend/generated/schema.graphql @@ -260,8 +260,6 @@ type EpisodeType implements EntityDescription { """How is this entity presented in the text?""" sourceMention: EventEpisodeSourceMentionChoices - - """locations involved in this episode""" spaces: [SpaceDescriptionType!]! """full description of the events in the passage"""