Skip to content

Commit 3b8ba70

Browse files
authoredJul 19, 2022
feat: move UI view API from Gateway to Hasura (NASA-AMMOS#61)
- Replace Gateway UI view queries with Hasura queries - Update Gateway UI view effects - Reorganize UI view stores - Update e2e test banananation jar
1 parent e3b2ef9 commit 3b8ba70

17 files changed

+353
-308
lines changed
 

‎docker-compose-test.yml

-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ services:
3535
PORT: 9000
3636
POSTGRES_AERIE_MERLIN_DB: aerie_merlin
3737
POSTGRES_AERIE_SCHEDULER_DB: aerie_scheduler
38-
POSTGRES_AERIE_UI_DB: aerie_ui
3938
POSTGRES_HOST: postgres
4039
POSTGRES_PASSWORD: aerie
4140
POSTGRES_PORT: 5432
0 Bytes
Binary file not shown.

‎src/components/activity/ActivityTable.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
1010
let activityTable: ViewActivityTable;
1111
12-
$: activityTable = $view?.plan.activityTables.find(table => table.id === activityTableId);
12+
$: activityTable = $view?.definition.plan.activityTables.find(table => table.id === activityTableId);
1313
</script>
1414

1515
<Table

‎src/components/menus/GridMenu.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<svelte:options accessors={true} immutable={true} />
22

33
<script lang="ts">
4-
import { viewActions } from '../../stores/views';
4+
import { viewUpdateLayout } from '../../stores/views';
55
import Menu from './Menu.svelte';
66
import MenuItem from './MenuItem.svelte';
77
@@ -11,7 +11,7 @@
1111
let gridMenu: Menu;
1212
1313
function updateGridComponent(name: string) {
14-
viewActions.updateLayout(gridId, 'componentName', name);
14+
viewUpdateLayout(gridId, 'componentName', name);
1515
}
1616
</script>
1717

‎src/components/timeline/Timeline.svelte

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import { constraintViolations } from '../../stores/constraints';
88
import { maxTimeRange, viewTimeRange } from '../../stores/plan';
99
import { resources } from '../../stores/resources';
10-
import { view, viewActions } from '../../stores/views';
10+
import { view, viewUpdateRow, viewUpdateTimeline } from '../../stores/views';
1111
import { getDoyTime } from '../../utilities/time';
1212
import { getXScale, MAX_CANVAS_SIZE } from '../../utilities/timeline';
1313
import TimelineRow from './Row.svelte';
@@ -27,7 +27,7 @@
2727
let xAxisDiv: HTMLDivElement;
2828
let xAxisDrawHeight: number = 90;
2929
30-
$: timeline = $view?.plan.timelines.find(timeline => timeline.id === timelineId);
30+
$: timeline = $view?.definition.plan.timelines.find(timeline => timeline.id === timelineId);
3131
$: rows = timeline?.rows || [];
3232
$: drawWidth = clientWidth > 0 ? clientWidth - timeline?.marginLeft - timeline?.marginRight : 0;
3333
$: setRowsMaxHeight(timelineDiv, xAxisDiv);
@@ -63,7 +63,7 @@
6363
if (source === SOURCES.POINTER) {
6464
rowDragMoveDisabled = true;
6565
}
66-
viewActions.updateTimeline('rows', rows, timelineId);
66+
viewUpdateTimeline('rows', rows, timelineId);
6767
}
6868
6969
function onMouseDown(event: CustomEvent<MouseDown>) {
@@ -90,7 +90,7 @@
9090
function onUpdateRowHeight(event: CustomEvent<{ newHeight: number; rowId: number }>) {
9191
const { newHeight, rowId } = event.detail;
9292
if (newHeight < MAX_CANVAS_SIZE) {
93-
viewActions.updateRow('height', newHeight, timelineId, rowId);
93+
viewUpdateRow('height', newHeight, timelineId, rowId);
9494
}
9595
}
9696

‎src/components/timeline/form/LayerLineForm.svelte

+5-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { selectedLayer, viewActions } from '../../../stores/views';
2+
import { selectedLayer, viewUpdateLayer } from '../../../stores/views';
33
import CssGrid from '../../ui/CssGrid.svelte';
44
55
$: lineLayer = $selectedLayer as LineLayer | null;
@@ -8,25 +8,13 @@
88
{#if lineLayer && lineLayer.chartType === 'line'}
99
<fieldset>
1010
<label for="yAxisId">Y-Axis Id</label>
11-
<input
12-
class="st-input w-100"
13-
name="yAxisId"
14-
type="number"
15-
value={lineLayer.yAxisId}
16-
on:input={viewActions.updateLayer}
17-
/>
11+
<input class="st-input w-100" name="yAxisId" type="number" value={lineLayer.yAxisId} on:input={viewUpdateLayer} />
1812
</fieldset>
1913

2014
<CssGrid columns="33% 33% 33%">
2115
<fieldset>
2216
<label for="lineColor">Line Color</label>
23-
<input
24-
class="w-100"
25-
name="lineColor"
26-
type="color"
27-
value={lineLayer.lineColor}
28-
on:input={viewActions.updateLayer}
29-
/>
17+
<input class="w-100" name="lineColor" type="color" value={lineLayer.lineColor} on:input={viewUpdateLayer} />
3018
</fieldset>
3119

3220
<fieldset>
@@ -36,7 +24,7 @@
3624
name="lineWidth"
3725
type="number"
3826
value={lineLayer.lineWidth}
39-
on:input={viewActions.updateLayer}
27+
on:input={viewUpdateLayer}
4028
/>
4129
</fieldset>
4230

@@ -47,7 +35,7 @@
4735
name="pointRadius"
4836
type="number"
4937
value={lineLayer.pointRadius}
50-
on:input={viewActions.updateLayer}
38+
on:input={viewUpdateLayer}
5139
/>
5240
</fieldset>
5341
</CssGrid>

‎src/components/timeline/form/LayerXRangeForm.svelte

+3-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { selectedLayer, viewActions } from '../../../stores/views';
2+
import { selectedLayer, viewUpdateLayer } from '../../../stores/views';
33
import CssGrid from '../../ui/CssGrid.svelte';
44
55
const colorSchemes = [
@@ -22,12 +22,7 @@
2222
<CssGrid columns="50% 50%">
2323
<fieldset>
2424
<label for="colorScheme">Color Scheme</label>
25-
<select
26-
class="st-select w-100"
27-
name="colorScheme"
28-
value={lineLayer.colorScheme}
29-
on:change={viewActions.updateLayer}
30-
>
25+
<select class="st-select w-100" name="colorScheme" value={lineLayer.colorScheme} on:change={viewUpdateLayer}>
3126
{#each colorSchemes as colorScheme}
3227
<option value={colorScheme.value}>
3328
{colorScheme.name}
@@ -38,13 +33,7 @@
3833

3934
<fieldset>
4035
<label for="opacity">Opacity</label>
41-
<input
42-
class="st-input w-100"
43-
name="opacity"
44-
type="number"
45-
value={lineLayer.opacity}
46-
on:input={viewActions.updateLayer}
47-
/>
36+
<input class="st-input w-100" name="opacity" type="number" value={lineLayer.opacity} on:input={viewUpdateLayer} />
4837
</fieldset>
4938
</CssGrid>
5039
{/if}

‎src/components/timeline/form/TimelineForm.svelte

+13-14
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
selectedTimelineId,
1212
selectedYAxisId,
1313
view,
14-
viewActions,
14+
viewSetSelectedRow,
15+
viewSetSelectedTimeline,
16+
viewUpdateLayer,
17+
viewUpdateRow,
18+
viewUpdateTimeline,
1519
} from '../../../stores/views';
1620
import { getTarget } from '../../../utilities/generic';
1721
import GridMenu from '../../menus/GridMenu.svelte';
@@ -27,20 +31,20 @@
2731
function updateRowEvent(event: Event) {
2832
event.stopPropagation();
2933
const { name, value } = getTarget(event);
30-
viewActions.updateRow(name, value);
34+
viewUpdateRow(name, value);
3135
}
3236
3337
function updateTimelineEvent(event: Event) {
3438
event.stopPropagation();
3539
const { name, value } = getTarget(event);
36-
viewActions.updateTimeline(name, value);
40+
viewUpdateTimeline(name, value);
3741
}
3842
3943
onMount(() => {
4044
if ($selectedTimelineId === null) {
41-
const firstTimeline = $view.plan.timelines[0];
45+
const firstTimeline = $view.definition.plan.timelines[0];
4246
if (firstTimeline) {
43-
viewActions.setSelectedTimeline(firstTimeline.id);
47+
viewSetSelectedTimeline(firstTimeline.id);
4448
}
4549
}
4650
});
@@ -61,10 +65,10 @@
6165
value={$selectedTimelineId}
6266
on:change={e => {
6367
const { valueAsNumber: id } = getTarget(e);
64-
viewActions.setSelectedTimeline(id);
68+
viewSetSelectedTimeline(id);
6569
}}
6670
>
67-
{#each $view.plan.timelines as timeline}
71+
{#each $view.definition.plan.timelines as timeline}
6872
<option value={timeline.id}>
6973
Timeline {timeline.id}
7074
</option>
@@ -114,7 +118,7 @@
114118
value={$selectedRowId}
115119
on:change={e => {
116120
const { valueAsNumber: id } = getTarget(e);
117-
viewActions.setSelectedRow(id);
121+
viewSetSelectedRow(id);
118122
}}
119123
>
120124
{#each $selectedTimeline.rows as row}
@@ -202,12 +206,7 @@
202206
<!-- Layer Chart Type. -->
203207
<fieldset>
204208
<label for="chartType">Chart Type</label>
205-
<select
206-
class="st-select w-100"
207-
name="chartType"
208-
value={$selectedLayer.chartType}
209-
on:change={viewActions.updateLayer}
210-
>
209+
<select class="st-select w-100" name="chartType" value={$selectedLayer.chartType} on:change={viewUpdateLayer}>
211210
<option value="activity"> Activity </option>
212211
<option value="line"> Line </option>
213212
<option value="x-range"> X-Range </option>

‎src/components/timeline/form/YAxisForm.svelte

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { selectedRow, selectedYAxis, viewActions } from '../../../stores/views';
2+
import { selectedRow, selectedYAxis, viewUpdateYAxis } from '../../../stores/views';
33
import { getTarget } from '../../../utilities/generic';
44
import CssGrid from '../../ui/CssGrid.svelte';
55
@@ -14,7 +14,7 @@
1414
1515
function updateAxis(event: Event) {
1616
const { name: prop, value } = getTarget(event);
17-
viewActions.updateYAxis(prop, value);
17+
viewUpdateYAxis(prop, value);
1818
}
1919
2020
function updateId(event: Event) {
@@ -26,7 +26,7 @@
2626
} else if (value === axesIdsMap[value]) {
2727
idError = `Y-Axis with id=${value} already exists`;
2828
} else {
29-
viewActions.updateYAxis('id', value);
29+
viewUpdateYAxis('id', value);
3030
idError = null;
3131
}
3232
} else {
@@ -37,7 +37,7 @@
3737
function updateLabel(event: Event) {
3838
const { name, value } = getTarget(event);
3939
const label: Label = { ...$selectedYAxis.label, [name]: value };
40-
viewActions.updateYAxis('label', label);
40+
viewUpdateYAxis('label', label);
4141
}
4242
4343
function updateScaleDomain(event: Event) {
@@ -56,9 +56,9 @@
5656
5757
const [min, max] = scaleDomain;
5858
if (min === null && max === null) {
59-
viewActions.updateYAxis('scaleDomain', []);
59+
viewUpdateYAxis('scaleDomain', []);
6060
} else {
61-
viewActions.updateYAxis('scaleDomain', scaleDomain);
61+
viewUpdateYAxis('scaleDomain', scaleDomain);
6262
}
6363
}
6464
</script>

‎src/components/ui/IFrame.svelte

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
88
let iFrame: ViewIFrame;
99
10-
$: iFrame = $view?.plan.iFrames.find(iFrame => iFrame.id === iFrameId);
10+
$: iFrame = $view?.definition.plan.iFrames.find(iFrame => iFrame.id === iFrameId);
1111
</script>
1212

1313
<iframe allow="fullscreen" src={iFrame?.src} title="iframe-{iFrame?.title}" />

‎src/components/view/ViewEditor.svelte

+30-15
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,40 @@
11
<script lang="ts">
22
import { session } from '$app/stores';
3-
import { view, viewText } from '../../stores/views';
3+
import { view, viewDefinitionText } from '../../stores/views';
44
import effects from '../../utilities/effects';
55
import GridMenu from '../menus/GridMenu.svelte';
66
import MonacoEditor from '../ui/MonacoEditor.svelte';
77
import Panel from '../ui/Panel.svelte';
88
99
export let gridId: number;
1010
11-
let name: string = '';
11+
let saveAsViewDisabled: boolean = true;
12+
let saveViewDisabled: boolean = true;
1213
13-
$: saveViewDisabled = $view?.meta?.owner !== $session?.user?.id;
14+
$: saveAsViewDisabled = $view?.name === '';
15+
$: saveViewDisabled = $view?.name === '' || $view?.owner !== $session?.user?.id;
1416
1517
function onDidChangeModelContent(event: CustomEvent<{ value: string }>): void {
1618
const { detail } = event;
1719
const { value } = detail;
1820
1921
try {
20-
const newView = JSON.parse(value);
21-
$view = newView;
22+
const definition: ViewDefinition = JSON.parse(value);
23+
$view = { ...$view, definition };
2224
} catch (e) {
2325
console.log(e);
2426
}
2527
}
2628
2729
function saveAsView() {
28-
const newView = { ...$view, name };
29-
effects.createView(newView);
30+
if ($view && !saveAsViewDisabled) {
31+
effects.createView($view.name, $session?.user?.id, $view.definition);
32+
}
3033
}
3134
3235
function saveView() {
33-
if ($view?.meta?.owner === $session.user.id) {
34-
effects.updateView($view);
36+
if ($view && $view.owner === $session.user.id && !saveViewDisabled) {
37+
effects.updateView($view.id, { definition: $view.definition, name: $view.name });
3538
}
3639
}
3740
</script>
@@ -44,18 +47,30 @@
4447
<i class="bi bi-save" style="font-size: 0.8rem" />
4548
Save
4649
</button>
47-
<button class="st-button secondary ellipsis" disabled={name === ''} on:click={saveAsView}>
50+
<button class="st-button secondary ellipsis" disabled={saveAsViewDisabled} on:click={saveAsView}>
4851
<i class="bi bi-save-fill" style="font-size: 0.8rem" />
4952
Save As
5053
</button>
5154
</div>
5255
</svelte:fragment>
5356

5457
<svelte:fragment slot="body">
55-
<fieldset>
56-
<label for="type">View Name</label>
57-
<input bind:value={name} autocomplete="off" class="st-input w-100" name="name" required type="text" />
58-
</fieldset>
58+
<div class="pb-2">
59+
<fieldset>
60+
<label for="id">ID</label>
61+
<input bind:value={$view.id} class="st-input w-100" disabled name="id" required type="text" />
62+
</fieldset>
63+
64+
<fieldset>
65+
<label for="owner">Owner</label>
66+
<input bind:value={$view.owner} class="st-input w-100" disabled name="owner" required type="text" />
67+
</fieldset>
68+
69+
<fieldset>
70+
<label for="name">Name</label>
71+
<input bind:value={$view.name} autocomplete="off" class="st-input w-100" name="name" required type="text" />
72+
</fieldset>
73+
</div>
5974

6075
<MonacoEditor
6176
automaticLayout={true}
@@ -64,7 +79,7 @@
6479
minimap={{ enabled: false }}
6580
scrollBeyondLastLine={false}
6681
tabSize={2}
67-
value={$viewText}
82+
value={$viewDefinitionText}
6883
on:didChangeModelContent={onDidChangeModelContent}
6984
/>
7085
</svelte:fragment>

‎src/components/view/Views.svelte

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<svelte:options immutable={true} />
22

33
<script lang="ts">
4+
import { session } from '$app/stores';
45
import { onMount } from 'svelte';
56
import { view, viewLayout } from '../../stores/views';
67
import effects from '../../utilities/effects';
@@ -19,26 +20,26 @@
1920
});
2021
2122
async function deleteView(viewId: number) {
22-
const { nextView, success } = await effects.deleteView(viewId);
23+
const success = await effects.deleteView(viewId);
2324
2425
if (success) {
2526
views = views.filter(v => v.id !== viewId);
2627
if ($view.id === viewId) {
27-
// If we deleted the view we are viewing, switch to the next view.
28+
const nextView = await effects.getView($session?.user?.id, null);
2829
$view = { ...nextView };
29-
$viewLayout = { ...nextView.plan.layout };
30+
$viewLayout = { ...nextView.definition.plan.layout };
3031
setQueryParam('viewId', `${nextView.id}`);
3132
}
3233
}
3334
}
3435
3536
async function loadView(viewId: number) {
3637
const query = new URLSearchParams(`?viewId=${viewId}`);
37-
const newView = await effects.getView(query);
38+
const newView = await effects.getView($session?.user?.id, query);
3839
3940
if (view) {
4041
$view = { ...newView };
41-
$viewLayout = { ...newView.plan.layout };
42+
$viewLayout = { ...newView.definition.plan.layout };
4243
setQueryParam('viewId', `${newView.id}`);
4344
} else {
4445
console.log(`No view found for ID: ${viewId}`);
@@ -58,15 +59,15 @@
5859
columnDefs={[
5960
{ field: 'id', name: 'ID', sortable: true },
6061
{ field: 'name', name: 'Name', sortable: true },
61-
{ field: 'meta.owner', name: 'Owner', sortable: true },
62-
{ field: 'meta.timeUpdated', name: 'Last Updated', sortable: true },
62+
{ field: 'owner', name: 'Owner', sortable: true },
63+
{ field: 'updated_at', name: 'Last Updated', sortable: true },
6364
]}
6465
rowActions
6566
rowData={views}
6667
on:rowClick={({ detail }) => loadView(detail.id)}
6768
>
6869
<span slot="actions-cell">
69-
{#if currentRow?.meta?.owner !== 'system'}
70+
{#if currentRow?.owner !== 'system'}
7071
<button
7172
class="st-button icon"
7273
on:click|stopPropagation={() => deleteView(currentRow.id)}

‎src/routes/plans/[id].svelte

+16-16
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
simulationStatus,
3939
simulationTemplates,
4040
} from '../../stores/simulation';
41-
import { view, viewActions, viewLayout } from '../../stores/views';
41+
import { view, viewLayout, viewSetLayout, viewUpdateLayout } from '../../stores/views';
4242
import effects from '../../utilities/effects';
4343
import { setQueryParam } from '../../utilities/generic';
4444
import { getUnixEpochTime } from '../../utilities/time';
@@ -58,7 +58,7 @@
5858
const initialPlan = await effects.getPlan(planId);
5959
6060
if (initialPlan) {
61-
const initialView = await effects.getView(url.searchParams);
61+
const initialView = await effects.getView(session.user.id, url.searchParams);
6262
6363
return {
6464
props: {
@@ -113,7 +113,7 @@
113113
114114
$: if (initialView) {
115115
$view = { ...initialView };
116-
$viewLayout = { ...initialView.plan.layout };
116+
$viewLayout = { ...initialView.definition.plan.layout };
117117
}
118118
119119
onMount(() => {
@@ -134,13 +134,13 @@
134134
function changeColumnSizes(event: CustomEvent<GridChangeSizesEvent>): void {
135135
const { detail } = event;
136136
const { gridId, newSizes } = detail;
137-
viewActions.updateLayout(gridId, 'columnSizes', newSizes);
137+
viewUpdateLayout(gridId, 'columnSizes', newSizes);
138138
}
139139
140140
function changeRowSizes(event: CustomEvent<GridChangeSizesEvent>): void {
141141
const { detail } = event;
142142
const { gridId, newSizes } = detail;
143-
viewActions.updateLayout(gridId, 'rowSizes', newSizes);
143+
viewUpdateLayout(gridId, 'rowSizes', newSizes);
144144
}
145145
146146
function onKeydown(event: KeyboardEvent): void {
@@ -163,42 +163,42 @@
163163
<svelte:fragment slot="right">
164164
<NavButton
165165
icon="si si-activity"
166-
selected={$view.plan.layout?.gridName === 'Activities'}
166+
selected={$view.definition.plan.layout?.gridName === 'Activities'}
167167
title="Activities"
168-
on:click={() => viewActions.setLayout('Activities')}
168+
on:click={() => viewSetLayout('Activities')}
169169
/>
170170
<NavButton
171171
icon="bi bi-braces-asterisk"
172-
selected={$view.plan.layout?.gridName === 'Constraints'}
172+
selected={$view.definition.plan.layout?.gridName === 'Constraints'}
173173
status={$checkConstraintsStatus}
174174
title="Constraints"
175-
on:click={() => viewActions.setLayout('Constraints')}
175+
on:click={() => viewSetLayout('Constraints')}
176176
/>
177177
<NavButton
178178
icon="bi bi-calendar3"
179-
selected={$view.plan.layout?.gridName === 'Scheduling'}
179+
selected={$view.definition.plan.layout?.gridName === 'Scheduling'}
180180
status={$schedulingStatus}
181181
title="Scheduling"
182-
on:click={() => viewActions.setLayout('Scheduling')}
182+
on:click={() => viewSetLayout('Scheduling')}
183183
/>
184184
<NavButton
185185
icon="bi bi-gear-wide-connected"
186-
selected={$view.plan.layout?.gridName === 'Simulation'}
186+
selected={$view.definition.plan.layout?.gridName === 'Simulation'}
187187
status={$simulationStatus}
188188
title="Simulation"
189-
on:click={() => viewActions.setLayout('Simulation')}
189+
on:click={() => viewSetLayout('Simulation')}
190190
/>
191191
<NavButton
192192
icon="bi bi-columns"
193-
selected={$view.plan.layout?.gridName === 'View'}
193+
selected={$view.definition.plan.layout?.gridName === 'View'}
194194
title="View"
195-
on:click={() => viewActions.setLayout('View')}
195+
on:click={() => viewSetLayout('View')}
196196
/>
197197
</svelte:fragment>
198198
</Nav>
199199

200200
<SplitGrid
201-
grid={$view?.plan.layout}
201+
grid={$view?.definition.plan.layout}
202202
{gridComponentsByName}
203203
on:changeColumnSizes={changeColumnSizes}
204204
on:changeRowSizes={changeRowSizes}

‎src/stores/views.ts

+133-124
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,27 @@ import { derived, get, writable, type Writable } from 'svelte/store';
22
import { getTarget } from '../utilities/generic';
33
import { activitiesGrid, constraintsGrid, schedulingGrid, simulationGrid, updateGrid } from '../utilities/grid';
44

5-
/* Stores. */
5+
/* Writeable. */
66

7-
/**
8-
* Current user-defined view.
9-
*/
107
export const view: Writable<View | null> = writable(null);
118

12-
/**
13-
* Current user-defined layout.
14-
*/
159
export const viewLayout: Writable<Grid | null> = writable(null);
1610

17-
/**
18-
* Formatted JSON string of the current view.
19-
*/
20-
export const viewText = derived(view, $view => ($view ? JSON.stringify($view, null, 2) : ''));
11+
export const selectedLayerId: Writable<number | null> = writable(null);
12+
13+
export const selectedRowId: Writable<number | null> = writable(null);
2114

2215
export const selectedTimelineId: Writable<number | null> = writable(null);
2316

17+
export const selectedYAxisId: Writable<number | null> = writable(null);
18+
19+
/* Derived. */
20+
21+
export const viewDefinitionText = derived(view, $view => ($view ? JSON.stringify($view.definition, null, 2) : ''));
22+
2423
export const selectedTimeline = derived([view, selectedTimelineId], ([$view, $selectedTimelineId]) => {
2524
if ($view !== null && $selectedTimelineId !== null) {
26-
for (const timeline of $view.plan.timelines) {
25+
for (const timeline of $view.definition.plan.timelines) {
2726
if (timeline && timeline.id === $selectedTimelineId) {
2827
return timeline;
2928
}
@@ -32,8 +31,6 @@ export const selectedTimeline = derived([view, selectedTimelineId], ([$view, $se
3231
return null;
3332
});
3433

35-
export const selectedRowId: Writable<number | null> = writable(null);
36-
3734
export const selectedRow = derived([selectedTimeline, selectedRowId], ([$selectedTimeline, $selectedRowId]) => {
3835
if ($selectedTimeline !== null) {
3936
for (const row of $selectedTimeline.rows) {
@@ -45,8 +42,6 @@ export const selectedRow = derived([selectedTimeline, selectedRowId], ([$selecte
4542
return null;
4643
});
4744

48-
export const selectedYAxisId: Writable<number | null> = writable(null);
49-
5045
export const selectedYAxis = derived([selectedRow, selectedYAxisId], ([$selectedRow, $selectedYAxisId]) => {
5146
if ($selectedRow !== null) {
5247
for (const yAxis of $selectedRow.yAxes) {
@@ -58,8 +53,6 @@ export const selectedYAxis = derived([selectedRow, selectedYAxisId], ([$selected
5853
return null;
5954
});
6055

61-
export const selectedLayerId: Writable<number | null> = writable(null);
62-
6356
export const selectedLayer = derived([selectedRow, selectedLayerId], ([$selectedRow, $selectedLayerId]) => {
6457
if ($selectedRow !== null) {
6558
for (const layer of $selectedRow.layers) {
@@ -71,85 +64,89 @@ export const selectedLayer = derived([selectedRow, selectedLayerId], ([$selected
7164
return null;
7265
});
7366

74-
/* Action Functions. */
75-
76-
export const viewActions = {
77-
setLayout(title: string) {
78-
let layout: Grid;
79-
80-
if (title === 'Activities') {
81-
layout = activitiesGrid;
82-
} else if (title === 'Constraints') {
83-
layout = constraintsGrid;
84-
} else if (title === 'Scheduling') {
85-
layout = schedulingGrid;
86-
} else if (title === 'Simulation') {
87-
layout = simulationGrid;
88-
} else if (title === 'View') {
89-
const viewGrid = get(viewLayout);
90-
layout = viewGrid;
91-
} else {
92-
layout = activitiesGrid;
93-
}
67+
/* Helper Functions. */
68+
69+
export function viewSetLayout(title: string) {
70+
let layout: Grid;
71+
72+
if (title === 'Activities') {
73+
layout = activitiesGrid;
74+
} else if (title === 'Constraints') {
75+
layout = constraintsGrid;
76+
} else if (title === 'Scheduling') {
77+
layout = schedulingGrid;
78+
} else if (title === 'Simulation') {
79+
layout = simulationGrid;
80+
} else if (title === 'View') {
81+
const viewGrid = get(viewLayout);
82+
layout = viewGrid;
83+
} else {
84+
layout = activitiesGrid;
85+
}
9486

95-
view.update(currentView => ({
96-
...currentView,
87+
view.update(currentView => ({
88+
...currentView,
89+
definition: {
90+
...currentView.definition,
9791
plan: {
98-
...currentView.plan,
92+
...currentView.definition.plan,
9993
layout,
10094
},
101-
}));
102-
},
103-
104-
setSelectedRow(rowId: number | null): void {
105-
selectedRowId.set(rowId);
106-
const currentRow = get(selectedRow);
107-
108-
if (currentRow) {
109-
const firstLayer = currentRow.layers[0];
110-
if (firstLayer) {
111-
selectedLayerId.set(firstLayer.id);
112-
} else {
113-
selectedLayerId.set(null);
114-
}
95+
},
96+
}));
97+
}
98+
99+
export function viewSetSelectedRow(rowId: number | null): void {
100+
selectedRowId.set(rowId);
101+
const currentRow = get(selectedRow);
102+
103+
if (currentRow) {
104+
const firstLayer = currentRow.layers[0];
105+
if (firstLayer) {
106+
selectedLayerId.set(firstLayer.id);
107+
} else {
108+
selectedLayerId.set(null);
109+
}
115110

116-
const firstYAxis = currentRow.yAxes[0];
117-
if (firstYAxis) {
118-
selectedYAxisId.set(firstYAxis.id);
119-
} else {
120-
selectedYAxisId.set(null);
121-
}
111+
const firstYAxis = currentRow.yAxes[0];
112+
if (firstYAxis) {
113+
selectedYAxisId.set(firstYAxis.id);
122114
} else {
123-
selectedRowId.set(null);
115+
selectedYAxisId.set(null);
124116
}
125-
},
117+
} else {
118+
selectedRowId.set(null);
119+
}
120+
}
126121

127-
setSelectedTimeline(timelineId: number | null): void {
128-
selectedTimelineId.set(timelineId);
129-
const currentTimeline = get(selectedTimeline);
122+
export function viewSetSelectedTimeline(timelineId: number | null): void {
123+
selectedTimelineId.set(timelineId);
124+
const currentTimeline = get(selectedTimeline);
130125

131-
if (currentTimeline) {
132-
const firstRow = currentTimeline.rows[0];
126+
if (currentTimeline) {
127+
const firstRow = currentTimeline.rows[0];
133128

134-
if (firstRow) {
135-
viewActions.setSelectedRow(firstRow.id);
136-
}
129+
if (firstRow) {
130+
viewSetSelectedRow(firstRow.id);
137131
}
138-
},
132+
}
133+
}
139134

140-
updateLayer(event: Event) {
141-
event.stopPropagation();
142-
const { name: prop, value } = getTarget(event);
135+
export function viewUpdateLayer(event: Event) {
136+
event.stopPropagation();
137+
const { name: prop, value } = getTarget(event);
143138

144-
const timelineId = get<number | null>(selectedTimelineId);
145-
const rowId = get<number | null>(selectedRowId);
146-
const layerId = get<number | null>(selectedLayerId);
139+
const timelineId = get<number | null>(selectedTimelineId);
140+
const rowId = get<number | null>(selectedRowId);
141+
const layerId = get<number | null>(selectedLayerId);
147142

148-
view.update(currentView => ({
149-
...currentView,
143+
view.update(currentView => ({
144+
...currentView,
145+
definition: {
146+
...currentView.definition,
150147
plan: {
151-
...currentView.plan,
152-
timelines: currentView.plan.timelines.map(timeline => {
148+
...currentView.definition.plan,
149+
timelines: currentView.definition.plan.timelines.map(timeline => {
153150
if (timeline && timeline.id === timelineId) {
154151
return {
155152
...timeline,
@@ -175,28 +172,34 @@ export const viewActions = {
175172
return timeline;
176173
}),
177174
},
178-
}));
179-
},
180-
181-
updateLayout(id: number, prop: string, value: any) {
182-
view.update(currentView => ({
183-
...currentView,
175+
},
176+
}));
177+
}
178+
179+
export function viewUpdateLayout(id: number, prop: string, value: any) {
180+
view.update(currentView => ({
181+
...currentView,
182+
definition: {
183+
...currentView.definition,
184184
plan: {
185-
...currentView.plan,
186-
layout: updateGrid(currentView.plan.layout, id, prop, value),
185+
...currentView.definition.plan,
186+
layout: updateGrid(currentView.definition.plan.layout, id, prop, value),
187187
},
188-
}));
189-
},
190-
191-
updateRow(prop: string, value: any, timelineId?: number, rowId?: number) {
192-
timelineId = timelineId ?? get<number | null>(selectedTimelineId);
193-
rowId = rowId ?? get<number | null>(selectedRowId);
194-
195-
view.update(currentView => ({
196-
...currentView,
188+
},
189+
}));
190+
}
191+
192+
export function viewUpdateRow(prop: string, value: any, timelineId?: number, rowId?: number) {
193+
timelineId = timelineId ?? get<number | null>(selectedTimelineId);
194+
rowId = rowId ?? get<number | null>(selectedRowId);
195+
196+
view.update(currentView => ({
197+
...currentView,
198+
definition: {
199+
...currentView.definition,
197200
plan: {
198-
...currentView.plan,
199-
timelines: currentView.plan.timelines.map(timeline => {
201+
...currentView.definition.plan,
202+
timelines: currentView.definition.plan.timelines.map(timeline => {
200203
if (timeline && timeline.id === timelineId) {
201204
return {
202205
...timeline,
@@ -214,17 +217,20 @@ export const viewActions = {
214217
return timeline;
215218
}),
216219
},
217-
}));
218-
},
220+
},
221+
}));
222+
}
219223

220-
updateTimeline(prop: string, value: any, timelineId?: number) {
221-
timelineId = timelineId ?? get<number | null>(selectedTimelineId);
224+
export function viewUpdateTimeline(prop: string, value: any, timelineId?: number) {
225+
timelineId = timelineId ?? get<number | null>(selectedTimelineId);
222226

223-
view.update(currentView => ({
224-
...currentView,
227+
view.update(currentView => ({
228+
...currentView,
229+
definition: {
230+
...currentView.definition,
225231
plan: {
226-
...currentView.plan,
227-
timelines: currentView.plan.timelines.map(timeline => {
232+
...currentView.definition.plan,
233+
timelines: currentView.definition.plan.timelines.map(timeline => {
228234
if (timeline && timeline.id === timelineId) {
229235
return {
230236
...timeline,
@@ -234,19 +240,22 @@ export const viewActions = {
234240
return timeline;
235241
}),
236242
},
237-
}));
238-
},
239-
240-
updateYAxis(prop: string, value: any) {
241-
const timelineId = get<number | null>(selectedTimelineId);
242-
const rowId = get<number | null>(selectedRowId);
243-
const yAxisId = get<number | null>(selectedYAxisId);
244-
245-
view.update(currentView => ({
246-
...currentView,
243+
},
244+
}));
245+
}
246+
247+
export function viewUpdateYAxis(prop: string, value: any) {
248+
const timelineId = get<number | null>(selectedTimelineId);
249+
const rowId = get<number | null>(selectedRowId);
250+
const yAxisId = get<number | null>(selectedYAxisId);
251+
252+
view.update(currentView => ({
253+
...currentView,
254+
definition: {
255+
...currentView.definition,
247256
plan: {
248-
...currentView.plan,
249-
timelines: currentView.plan.timelines.map(timeline => {
257+
...currentView.definition.plan,
258+
timelines: currentView.definition.plan.timelines.map(timeline => {
250259
if (timeline && timeline.id === timelineId) {
251260
return {
252261
...timeline,
@@ -275,6 +284,6 @@ export const viewActions = {
275284
return timeline;
276285
}),
277286
},
278-
}));
279-
},
280-
};
287+
},
288+
}));
289+
}

‎src/types/view.d.ts

+14-36
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,3 @@
1-
type CreateViewResponse = {
2-
errors: any[] | null;
3-
message: string;
4-
success: boolean;
5-
view: View | null;
6-
};
7-
8-
type DeleteViewResponse = {
9-
message: string;
10-
nextView: View | null;
11-
success: boolean;
12-
};
13-
14-
type GetViewResponse = {
15-
message: string;
16-
success: boolean;
17-
view: View | null;
18-
};
19-
20-
type UpdateViewResponse = {
21-
errors: any[] | null;
22-
message: string;
23-
success: boolean;
24-
};
25-
261
type ViewActivityTable = {
272
columnDefs: ColumnDef[];
283
id: number;
@@ -34,23 +9,26 @@ type ViewIFrame = {
349
title: string;
3510
};
3611

37-
type ViewMeta = {
12+
type ViewInsertInput = {
13+
definition: ViewDefinition;
14+
name: string;
3815
owner: string;
39-
timeCreated: number;
40-
timeUpdated: number;
41-
version: string;
4216
};
4317

44-
type ViewPlan = {
45-
activityTables: ViewActivityTable[];
46-
iFrames: ViewIFrame[];
47-
layout: Grid;
48-
timelines: Timeline[];
18+
type ViewDefinition = {
19+
plan: {
20+
activityTables: ViewActivityTable[];
21+
iFrames: ViewIFrame[];
22+
layout: Grid;
23+
timelines: Timeline[];
24+
};
4925
};
5026

5127
type View = {
28+
created_at: string;
29+
definition: ViewDefinition;
5230
id: number;
53-
meta: ViewMeta;
5431
name: string;
55-
plan: ViewPlan;
32+
owner: string;
33+
updated_at: string;
5634
};

‎src/utilities/effects.ts

+43-49
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { schedulingStatus, selectedSpecId } from '../stores/scheduling';
1717
import { simulation, simulationStatus } from '../stores/simulation';
1818
import { view } from '../stores/views';
1919
import { activityDirectiveToActivity, activitySimulatedToActivity, getChildIdsFn, getParentIdFn } from './activities';
20-
import { setQueryParam, sleep } from './generic';
20+
import { parseFloatOrNull, setQueryParam, sleep } from './generic';
2121
import gql from './gql';
2222
import { showConfirmModal } from './modal';
2323
import { reqGateway, reqHasura } from './requests';
@@ -336,26 +336,15 @@ const effects = {
336336
}
337337
},
338338

339-
async createView(viewInput: View): Promise<void> {
339+
async createView(name: string, owner: string, definition: ViewDefinition): Promise<void> {
340340
try {
341-
const data = await reqGateway<CreateViewResponse>(
342-
'/view',
343-
'POST',
344-
JSON.stringify({ view: viewInput }),
345-
null,
346-
false,
347-
);
348-
const { errors, message, success, view: newView } = data;
341+
const viewInsertInput: ViewInsertInput = { definition, name, owner };
342+
const data = await reqHasura<View>(gql.CREATE_VIEW, { view: viewInsertInput });
343+
const { newView } = data;
349344

350-
if (success) {
351-
view.update(() => newView);
352-
setQueryParam('viewId', `${newView.id}`);
353-
showSuccessToast('View Created Successfully');
354-
} else {
355-
console.log(errors);
356-
console.log(message);
357-
showFailureToast('View Create Failed');
358-
}
345+
view.update(() => newView);
346+
setQueryParam('viewId', `${newView.id}`);
347+
showSuccessToast('View Created Successfully');
359348
} catch (e) {
360349
console.log(e);
361350
showFailureToast('View Create Failed');
@@ -570,19 +559,17 @@ const effects = {
570559
}
571560
},
572561

573-
async deleteView(id: number): Promise<DeleteViewResponse> {
562+
async deleteView(id: number): Promise<boolean> {
574563
try {
575564
const confirm = await showConfirmModal('Delete', 'Are you sure you want to delete this view?', 'Delete View');
576565

577566
if (confirm) {
578-
const data = await reqGateway<DeleteViewResponse>(`/view/${id}`, 'DELETE', null, null, false);
579-
return data;
567+
await reqHasura(gql.DELETE_VIEW, { id });
568+
return true;
580569
}
581-
582-
return { message: 'Delete view canceled', nextView: null, success: false };
583570
} catch (e) {
584571
console.log(e);
585-
return { message: 'An unexpected error occurred', nextView: null, success: false };
572+
return false;
586573
}
587574
},
588575

@@ -961,12 +948,30 @@ const effects = {
961948
}
962949
},
963950

964-
async getView(query: URLSearchParams): Promise<View | null> {
951+
async getView(owner: string, query: URLSearchParams | null): Promise<View | null> {
965952
try {
966-
const viewId = query.has('viewId') ? query.get('viewId') : 'latest';
967-
const data = await reqGateway<{ view: View }>(`/view/${viewId}`, 'GET', null, null, false);
968-
const { view } = data;
969-
return view;
953+
if (query !== null) {
954+
const viewId = query.has('viewId') ? query.get('viewId') : null;
955+
const viewIdAsNumber = parseFloatOrNull(viewId);
956+
957+
if (viewIdAsNumber !== null) {
958+
const data = await reqHasura<View>(gql.GET_VIEW, { id: viewIdAsNumber });
959+
const { view } = data;
960+
961+
if (view !== null) {
962+
return view;
963+
}
964+
}
965+
}
966+
967+
const data = await reqHasura<View[]>(gql.GET_VIEW_LATEST, { owner });
968+
const { views } = data;
969+
970+
if (views.length) {
971+
return views[0];
972+
} else {
973+
return null;
974+
}
970975
} catch (e) {
971976
console.log(e);
972977
return null;
@@ -975,8 +980,9 @@ const effects = {
975980

976981
async getViews(): Promise<View[]> {
977982
try {
978-
const data = await reqGateway<View[]>('/views', 'GET', null, null, false);
979-
return data;
983+
const data = await reqHasura<View[]>(gql.GET_VIEWS);
984+
const { views } = data;
985+
return views;
980986
} catch (e) {
981987
console.log(e);
982988
return [];
@@ -1274,27 +1280,15 @@ const effects = {
12741280
}
12751281
},
12761282

1277-
async updateView(view: View): Promise<void> {
1283+
async updateView(id: number, view: Partial<View>): Promise<boolean> {
12781284
try {
1279-
const data = await reqGateway<UpdateViewResponse>(
1280-
`/view/${view.id}`,
1281-
'PUT',
1282-
JSON.stringify({ view }),
1283-
null,
1284-
false,
1285-
);
1286-
const { errors, message, success } = data;
1287-
1288-
if (success) {
1289-
showSuccessToast('View Updated Successfully');
1290-
} else {
1291-
console.log(errors);
1292-
console.log(message);
1293-
showFailureToast('View Update Failed');
1294-
}
1285+
await reqHasura<Pick<View, 'id'>>(gql.UPDATE_VIEW, { id, view });
1286+
showSuccessToast('View Updated Successfully');
1287+
return true;
12951288
} catch (e) {
12961289
console.log(e);
12971290
showFailureToast('View Update Failed');
1291+
return false;
12981292
}
12991293
},
13001294

‎src/utilities/gql.ts

+73
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,19 @@ const gql = {
123123
}
124124
`,
125125

126+
CREATE_VIEW: `#graphql
127+
mutation CreateView($view: view_insert_input!) {
128+
newView: insert_view_one(object: $view) {
129+
created_at
130+
definition
131+
id
132+
name
133+
owner
134+
updated_at
135+
}
136+
}
137+
`,
138+
126139
DELETE_ACTIVITY: `#graphql
127140
mutation DeleteActivity($id: Int!) {
128141
deleteActivity: delete_activity_by_pk(id: $id) {
@@ -216,6 +229,14 @@ const gql = {
216229
}
217230
`,
218231

232+
DELETE_VIEW: `#graphql
233+
mutation DeleteView($id: Int!) {
234+
deletedView: delete_view_by_pk(id: $id) {
235+
id
236+
}
237+
}
238+
`,
239+
219240
EXPAND: `#graphql
220241
mutation Expand($expansionSetId: Int!, $simulationDatasetId: Int!) {
221242
expand: expandAllActivities(expansionSetId: $expansionSetId, simulationDatasetId: $simulationDatasetId) {
@@ -544,6 +565,48 @@ const gql = {
544565
}
545566
`,
546567

568+
GET_VIEW: `#graphql
569+
query GetView($id: Int!) {
570+
view: view_by_pk(id: $id) {
571+
created_at
572+
definition
573+
id
574+
name
575+
owner
576+
updated_at
577+
}
578+
}
579+
`,
580+
581+
GET_VIEWS: `#graphql
582+
query GetViews {
583+
views: view {
584+
created_at
585+
definition
586+
id
587+
name
588+
owner
589+
updated_at
590+
}
591+
}
592+
`,
593+
594+
GET_VIEW_LATEST: `#graphql
595+
query GetViewLatest($owner: String!) {
596+
views: view(
597+
where: { _or: [{ owner: { _eq: $owner } }, { owner: { _eq: "system" } }] },
598+
order_by: { updated_at: desc }
599+
) {
600+
created_at
601+
definition
602+
id
603+
name
604+
owner
605+
updated_at
606+
}
607+
}
608+
`,
609+
547610
INSERT_SEQUENCE_TO_ACTIVITY: `#graphql
548611
mutation InsertSequenceToActivity($input: sequence_to_simulated_activity_insert_input!) {
549612
sequence: insert_sequence_to_simulated_activity_one(
@@ -846,6 +909,16 @@ const gql = {
846909
}
847910
`,
848911

912+
UPDATE_VIEW: `#graphql
913+
mutation UpdateView($id: Int!, $view: view_set_input!) {
914+
updatedView: update_view_by_pk(
915+
pk_columns: { id: $id }, _set: $view
916+
) {
917+
id
918+
}
919+
}
920+
`,
921+
849922
VALIDATE_ACTIVITY_ARGUMENTS: `#graphql
850923
query ValidateActivityArguments($arguments: ActivityArguments!, $activityTypeName: String!, $modelId: ID!) {
851924
validateActivityArguments(

0 commit comments

Comments
 (0)
Please sign in to comment.