Skip to content

Commit

Permalink
form updates and type fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
vladyslav-tk committed Mar 5, 2025
1 parent e953b20 commit af18ac0
Show file tree
Hide file tree
Showing 8 changed files with 391 additions and 194 deletions.
296 changes: 206 additions & 90 deletions src/apps/survey/demoSurveyConfig.ts

Large diffs are not rendered by default.

67 changes: 36 additions & 31 deletions src/apps/survey/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type {HTMLTemplateResult, TemplateResult} from 'lit';
import type {HTMLTemplateResult} from 'lit';
import {html} from 'lit';
import {customElement, query, state} from 'lit/decorators.js';

import '../../structure/ngv-structure-app.js';
import {localized, msg} from '@lit/localize';
import {ABaseApp} from '../../structure/BaseApp.js';

import type {ISurveyConfig, Item, ItemSummary} from './ingv-config-survey.js';
import type {ISurveyConfig} from './ingv-config-survey.js';
import '../../plugins/cesium/ngv-plugin-cesium-widget';
import '../../plugins/cesium/ngv-plugin-cesium-upload';
import '../../plugins/cesium/ngv-plugin-cesium-model-interact';
Expand All @@ -32,28 +32,31 @@ import {
} from '@cesium/engine';
import type {ViewerInitializedDetails} from '../../plugins/cesium/ngv-plugin-cesium-widget.js';
import type {ClickDetail} from '../../plugins/cesium/ngv-plugin-cesium-click-info.js';
import type {FieldValues} from '../../plugins/ui/ngv-survey.js';
import {
getJson as readJsonFile,
getOrCreateDirectoryChain,
persistJson,
removeFile,
} from '../../utils/storage-utils.js';
import type {Coordinate} from '../../utils/generalTypes.js';
import type {Coordinate, FieldValues} from '../../utils/generalTypes.js';
import {Task} from '@lit/task';
import type {OfflineInfo} from '../../plugins/cesium/ngv-plugin-cesium-offline.js';
import type {NgvPluginCesiumNavigation} from '../../plugins/cesium/ngv-plugin-cesium-navigation.js';
import type {IngvCesiumContext} from '../../interfaces/cesium/ingv-cesium-context.js';
import {poolRunner} from '../../utils/pool-runner.js';
import type { LabelValue, SurveyField } from '../../interfaces/ui/ingv-survey.js';
import type {LabelValue, SurveyField} from '../../interfaces/ui/ingv-survey.js';
import type {config} from './demoSurveyConfig.js';

type ItemSummary =
typeof config extends ISurveyConfig<infer I, any> ? I : never;
type Item = typeof config extends ISurveyConfig<any, infer I> ? I : never;

const STORAGE_DIR = ['surveys'];
const STORAGE_LIST_NAME = 'surveys.json';

@customElement('ngv-app-survey')
@localized()
export class NgvAppSurvey extends ABaseApp<ISurveyConfig> {

export class NgvAppSurvey extends ABaseApp<typeof config> {
@state()
private viewer: CesiumWidget;
@state()
Expand All @@ -76,31 +79,30 @@ export class NgvAppSurvey extends ABaseApp<ISurveyConfig> {
private currentView: IngvCesiumContext['views'][number] | null = null;

private collections: ViewerInitializedDetails['primitiveCollections'];
private surveyFieldValues:
| Record<
string,
| string
| number
| Record<string, boolean>
| Coordinate
| TemplateResult<1>
>
| undefined = {};
private surveyFieldValues: FieldValues = {};

constructor() {
super(() => import('./demoSurveyConfig.js'));
}

async resolveFieldsConfig(offline: boolean, surveyFields: SurveyField[]) {
const promises = surveyFields.map(v => {
if ((v.type !== 'select' && v.type !== 'radio' && v.type !== 'checkbox') || typeof v.options !== 'function') {
async resolveFieldsConfig(
offline: boolean,
surveyFields: SurveyField[],
): Promise<void> {
const promises = surveyFields.map((v) => {
if (
(v.type !== 'select' && v.type !== 'radio' && v.type !== 'checkbox') ||
typeof v.options !== 'function'
) {
return null;
}
const filename = `field-${v.id}.json`;
if (offline) {
return readJsonFile<LabelValue[]>(this.persistentDir, filename).then(result => v.options = result);
return readJsonFile<LabelValue[]>(this.persistentDir, filename).then(
(result) => (v.options = result),
);
}
return v.options().then(async result => {
return v.options().then(async (result) => {
v.options = result;
await persistJson(this.persistentDir, filename, result);
return result;
Expand All @@ -111,10 +113,10 @@ export class NgvAppSurvey extends ABaseApp<ISurveyConfig> {

// @ts-expect-error TS6133
private _configChange = new Task(this, {
args: (): [ISurveyConfig] => [this.config],
args: (): [typeof config] => [this.config],
task: async ([config]) => {
if (config.app.cesiumContext.offline) {
const appName = config.app.cesiumContext.name
const appName = config.app.cesiumContext.name;
this.offline = localStorage.getItem(`${appName}_offline`) === 'true';
}

Expand Down Expand Up @@ -180,7 +182,7 @@ export class NgvAppSurvey extends ABaseApp<ISurveyConfig> {
await persistJson(this.persistentDir, STORAGE_LIST_NAME, this.surveys);
console.log('Persisted surveys');
} else {
console.log('Reading surveys from storage')
console.log('Reading surveys from storage');
this.surveys = await readJsonFile<ItemSummary[]>(
this.persistentDir,
STORAGE_LIST_NAME,
Expand Down Expand Up @@ -226,6 +228,8 @@ export class NgvAppSurvey extends ABaseApp<ISurveyConfig> {
}

addPoint(position: Cartesian3, id?: string): Entity {
const entExists = id && this.dataSource.entities.getById(id);
if (entExists) return entExists;
return this.dataSource.entities.add({
id,
position,
Expand Down Expand Up @@ -277,14 +281,14 @@ export class NgvAppSurvey extends ABaseApp<ISurveyConfig> {
if (!id) return;
if (!this.surveys.find((s) => s.id === id)) {
this.surveys.push({
...evt.detail,
id,
coordinates: [
coordinates.longitude,
coordinates.latitude,
coordinates.height,
],
lastModifiedMs: Date.now(), // FIXME timezone?
title: 'Some hardcoded title',
});
await persistJson(this.persistentDir, STORAGE_LIST_NAME, this.surveys);
}
Expand Down Expand Up @@ -333,12 +337,11 @@ export class NgvAppSurvey extends ABaseApp<ISurveyConfig> {
async getOrReadSurvey(id: string): Promise<FieldValues> {
let item: Item;
if (this.offline) {
item = await readJsonFile<Item>(this.persistentDir, `${id}.json`);
return await readJsonFile<FieldValues>(this.persistentDir, `${id}.json`);
} else {
item = await this.config.app.survey.getItem(id, {});
item = await this.config.app.survey.getItem({id});
return this.config.app.survey.itemToFields(item);
}
const survey = this.config.app.survey.itemToFields(item);
return survey;
}

highlightEntity(id: string): void {
Expand Down Expand Up @@ -384,7 +387,7 @@ export class NgvAppSurvey extends ABaseApp<ISurveyConfig> {
concurrency: 7,
tasks: this.surveys,
runTask: async (s) => {
const item = await this.config.app.survey.getItem(s.id, {});
const item = await this.config.app.survey.getItem({id: s.id});
await persistJson(this.persistentDir, `${item.id}.json`, item);
console.log('->', `${item.id}.json`, item);
// FIXME: here we can download all the photos (or append to a list to be downloaded later)
Expand Down Expand Up @@ -475,6 +478,8 @@ export class NgvAppSurvey extends ABaseApp<ISurveyConfig> {
? html` <ngv-survey
.surveyFields="${this.config.app.survey.fields}"
.fieldValues="${this.surveyFieldValues}"
.projection=${this.config.app.cesiumContext
.clickInfoOptions?.projection}
@confirm=${(evt: CustomEvent<FieldValues>) => {
this.confirm(evt).catch((e) => console.error(e));
}}
Expand Down
11 changes: 7 additions & 4 deletions src/apps/survey/ingv-config-survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@ import type {FieldValues} from '../../utils/generalTypes.js';
export interface ItemSummary {
id: string;
lastModifiedMs: number;
title: string;
coordinates: number[];
}

export interface Item extends ItemSummary {}
export type Context = {
id: string;
};

export type Item = ItemSummary;

export interface ISurveyConfig<ItemSummaryType = ItemSummary, ItemType = Item>
extends INgvStructureApp {
app: {
cesiumContext: IngvCesiumContext;
survey: {
listItems: (context: any) => Promise<ItemSummaryType[]>;
getItem: (id: string, context: any) => Promise<ItemType>;
listItems: (context: Context) => Promise<ItemSummaryType[]>;
getItem: (context: Context) => Promise<ItemType>;
itemToFields: (item: ItemType) => FieldValues;
fieldsToItem: (values: FieldValues) => Item;
fields: SurveyField[];
Expand Down
25 changes: 18 additions & 7 deletions src/interfaces/ui/ingv-survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export type LabelValue = {
label: string;
value: string;
checked?: boolean;
}
};

type SurveyFieldBase = {
required?: boolean;
Expand All @@ -18,30 +18,35 @@ export type SurveyInput = SurveyFieldBase & {
placeholder?: string;
min?: number | string;
max?: number | string;
inputType?: 'text' | 'number' | 'date';
inputType?: 'text' | 'number' | 'date' | 'datetime-local';
};

export type SurveyTextarea = Omit<SurveyInput, 'type' | 'inputType'> & {
type: 'textarea';
rows?: number;
};

type OptionsFunction = () => Promise<{label: string; value: string}[]>;
export type FieldOptions = LabelValue[] | Record<string, LabelValue[]>;

type OptionsFunction = () => Promise<FieldOptions>;

export type SurveySelect = SurveyFieldBase & {
type: 'select';
options: LabelValue[] | OptionsFunction;
options: FieldOptions | OptionsFunction;
keyPropId?: string;
};

export type SurveyRadio = Omit<SurveyFieldBase, 'defaultValue'> & {
type: 'radio';
defaultValue: string;
options: LabelValue[] | OptionsFunction;
options: FieldOptions | OptionsFunction;
keyPropId?: string;
};

export type SurveyCheckbox = Omit<SurveyFieldBase, 'defaultValue'> & {
type: 'checkbox';
options: LabelValue[] | OptionsFunction;
options: FieldOptions | OptionsFunction;
keyPropId?: string;
};

export type SurveyCoords = SurveyFieldBase & {
Expand All @@ -62,6 +67,11 @@ export type SurveyId = Omit<SurveyFieldBase, 'required' | 'defaultValue'> & {
type: 'id';
};

export type SurveyReadonly = Omit<SurveyFieldBase, 'required'> & {
type: 'readonly';
renderCallback?: (...args: any[]) => Promise<string>;
};

export interface SurveyConditional {
visible: (fields: SurveyField[]) => boolean;
children: SurveyField[];
Expand All @@ -79,5 +89,6 @@ export type SurveyField =
| SurveyCheckbox
| SurveyCoords
| SurveyFile
| SurveyId;
| SurveyId
| SurveyReadonly;
// | SurveyConditional;
1 change: 0 additions & 1 deletion src/plugins/cesium/draw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type {
LabelGraphics,
} from '@cesium/engine';
import {CallbackPositionProperty} from '@cesium/engine';
import {CallbackPositionProperty} from '@cesium/engine';
import {
EllipsoidGeodesic,
PositionProperty,
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/cesium/ngv-plugin-cesium-offline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class NgvPluginCesiumOffline extends LitElement {
@property({type: Object})
info: OfflineInfo;
@property({type: Object})
beforeSwitchDispatch: (goOffline: boolean) => {};
beforeSwitchDispatch: (goOffline: boolean) => Promise<void>;

@state()
offline: boolean = false;
Expand Down
Loading

0 comments on commit af18ac0

Please sign in to comment.