Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snapshot Panel #905

Merged
merged 18 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/components/Collapse.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
export let collapsible: boolean = true;
export let defaultExpanded: boolean = true;
export let error: boolean = false;
export let padContent: boolean = true;
export let title: string = '';
export let titleClassName: string = '';
export let tooltipContent: string = '';
Expand Down Expand Up @@ -57,7 +58,7 @@
<slot name="right" />
</div>
</button>
<div class="content" class:expanded aria-hidden={collapsible ? !expanded : false}>
<div class="content" class:pad-content={padContent} class:expanded aria-hidden={collapsible ? !expanded : false}>
<slot />
</div>
</div>
Expand Down Expand Up @@ -127,12 +128,15 @@
flex-direction: column;
gap: 4px;
height: 0;
margin-left: 32px;
overflow: hidden;
visibility: hidden;
width: 0;
}

.content.pad-content {
margin-left: 32px;
}

.expanded {
height: auto;
visibility: visible;
Expand Down
8 changes: 8 additions & 0 deletions src/components/menus/MenuDivider.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<hr class="menu-divider" />

<style>
hr.menu-divider {
opacity: 0.3;
width: 90%;
}
</style>
13 changes: 5 additions & 8 deletions src/components/menus/PlanMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { base } from '$app/paths';
import BranchIcon from '@nasa-jpl/stellar/icons/branch.svg?component';
import ChevronDownIcon from '@nasa-jpl/stellar/icons/chevron_down.svg?component';
import { viewTogglePanel } from '../../stores/views';
import type { User } from '../../types/app';
import type { Plan } from '../../types/plan';
import effects from '../../utilities/effects';
Expand All @@ -13,6 +14,7 @@
import { featurePermissions } from '../../utilities/permissions';
import Menu from '../menus/Menu.svelte';
import MenuItem from '../menus/MenuItem.svelte';
import MenuDivider from './MenuDivider.svelte';

export let plan: Plan;
export let user: User | null;
Expand All @@ -35,7 +37,7 @@
}

function viewSnapshotHistory() {
// TODO: open snapshot panel
viewTogglePanel({ state: true, type: 'right', update: { rightComponentTop: 'PlanMetadataPanel' } });
}

function showPlanBranches() {
Expand Down Expand Up @@ -65,7 +67,7 @@
<div class="column-name">View merge requests</div>
</MenuItem>
{#if plan.parent_plan !== null}
<hr class="menu-divider" />
<MenuDivider />
<MenuItem
on:click={createMergePlanBranchRequest}
use={[
Expand All @@ -84,7 +86,7 @@
<div class="column-name">Open parent plan</div>
</MenuItem>
{/if}
<hr class="menu-divider" />
<MenuDivider />
<MenuItem on:click={createPlanSnapshot}>
<div class="column-name">Take Snapshot</div>
</MenuItem>
Expand All @@ -101,11 +103,6 @@
</div>

<style>
hr.menu-divider {
opacity: 0.3;
width: 90%;
}

.plan-menu-container {
align-items: center;
display: flex;
Expand Down
54 changes: 47 additions & 7 deletions src/components/modals/CreatePlanSnapshotModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,36 @@

<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { planSnapshots } from '../../stores/planSnapshots';
import { tags } from '../../stores/tags';
import type { User } from '../../types/app';
import type { Plan } from '../../types/plan';
import type { Tag, TagsChangeEvent } from '../../types/tags';
import effects from '../../utilities/effects';
import TagsInput from '../ui/Tags/TagsInput.svelte';
import Modal from './Modal.svelte';
import ModalContent from './ModalContent.svelte';
import ModalFooter from './ModalFooter.svelte';
import ModalHeader from './ModalHeader.svelte';

export let height: number = 270;
export let width: number = 280;
export let height: number | string = 'unset';
export let width: number = 380;
export let plan: Plan;
export let user: User | null = null;

const dispatch = createEventDispatcher();

let snapshotName: string = `${plan.name} - Snapshot`;
let createButtonDisabled: boolean = true;
let snapshotName: string = `${plan.name} – Snapshot ${$planSnapshots.length + 1}`;
let snapshotDescription: string = '';
let snapshotTags: Tag[] = [];

/* TODO tie this into featurePermissions? */
$: createButtonDisabled = snapshotName === '';

function create() {
if (!createButtonDisabled) {
dispatch('create', { name: snapshotName, plan });
dispatch('create', { description: snapshotDescription, name: snapshotName, plan, tags: snapshotTags });
}
}

Expand All @@ -32,26 +42,56 @@
create();
}
}

async function onTagsInputChange(event: TagsChangeEvent) {
const {
detail: { tag, type },
} = event;
if (type === 'remove') {
snapshotTags = snapshotTags.filter(t => t.name !== tag.name);
} else if (type === 'create' || type === 'select') {
let tagsToAdd: Tag[] = [tag];
if (type === 'create') {
tagsToAdd = (await effects.createTags([{ color: tag.color, name: tag.name }], user)) || [];
}
snapshotTags = snapshotTags.concat(tagsToAdd);
}
}
</script>

<svelte:window on:keydown={onKeydown} />

<Modal {height} {width}>
<ModalHeader on:close>Take Snapshot</ModalHeader>
<ModalContent style=" display: flex; flex-direction: column; gap: 8px; padding:8px 0 0 ;">
<ModalContent style=" display: flex; flex-direction: column; gap: 8px; padding:8px 0 16px;">
<div class="description">Snapshot will capture activity directives and references to relevant simulations.</div>
<fieldset>
<label for="name">Name of snapshot</label>
<label for="name">Name of Snapshot</label>
<!-- svelte-ignore a11y-autofocus -->
<input
autofocus
bind:value={snapshotName}
placeholder="Name of snapshot"
placeholder="Name of Snapshot"
autocomplete="off"
class="st-input w-100"
name="name"
required
type="text"
/>
</fieldset>
<fieldset>
<label for="description">Description</label>
<textarea
bind:value={snapshotDescription}
placeholder="Notes about this snapshot"
class="st-input w-100"
name="description"
/>
</fieldset>
<fieldset>
<label for="plan-duration">Tags</label>
<TagsInput options={$tags} selected={snapshotTags} on:change={onTagsInputChange} />
</fieldset>
</ModalContent>
<ModalFooter>
<button class="st-button secondary" on:click={() => dispatch('close')}> Cancel </button>
Expand Down
13 changes: 12 additions & 1 deletion src/components/modals/Modal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,28 @@
export let height: number | string = 350;
export let width: number | string = 400;

let modal: HTMLDivElement | null = null;

$: heightStyle = typeof height === 'number' ? `${height}px` : height;
$: widthStyle = typeof width === 'number' ? `${width}px` : width;

function onClickOutside(event: MouseEvent | TouchEvent) {
// Stop propagation of events that originate from within the modal
// to prevent the modal from closing.
if (modal && modal.contains(event.target as Node)) {
event.stopPropagation();
}
}
</script>

<div class="modal-container">
<div
bind:this={modal}
class="modal st-typography-body"
role="none"
style:height={heightStyle}
style:width={widthStyle}
on:click|stopPropagation
on:click={onClickOutside}
>
<slot />
</div>
Expand Down
33 changes: 33 additions & 0 deletions src/components/plan/PlanForm.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
<svelte:options immutable={true} />

<script lang="ts">
import { SearchParameters } from '../../enums/searchParameters';
import { planSnapshotId, planSnapshotsWithSimulations } from '../../stores/planSnapshots';
import type { User } from '../../types/app';
import type { Plan } from '../../types/plan';
import type { PlanTagsInsertInput, Tag, TagsChangeEvent } from '../../types/tags';
import effects from '../../utilities/effects';
import { setQueryParam } from '../../utilities/generic';
import { permissionHandler } from '../../utilities/permissionHandler';
import { featurePermissions } from '../../utilities/permissions';
import { getShortISOForDate } from '../../utilities/time';
import { tooltip } from '../../utilities/tooltip';
import Collapse from '../Collapse.svelte';
import Input from '../form/Input.svelte';
import CardList from '../ui/CardList.svelte';
import TagsInput from '../ui/Tags/TagsInput.svelte';
import PlanSnapshot from './PlanSnapshot.svelte';

export let plan: Plan | null;
export let planTags: Tag[];
Expand Down Expand Up @@ -47,6 +52,13 @@
await effects.createPlanTags(newPlanTags, user, false);
}
}

function onCreatePlanSnapshot(event: MouseEvent) {
event.stopPropagation();
if (plan) {
effects.createPlanSnapshot(plan, user);
}
}
</script>

<div class="plan-form">
Expand Down Expand Up @@ -139,6 +151,27 @@
</Input>
</Collapse>
</fieldset>
<fieldset>
<Collapse title="Snapshots" padContent={false}>
<button class="st-button secondary" slot="right" on:click={onCreatePlanSnapshot}>Take Snapshot</button>
<div style="margin-top: 8px">
<CardList>
{#each $planSnapshotsWithSimulations as planSnapshot (planSnapshot.snapshot_id)}
<PlanSnapshot
activePlanSnapshotId={$planSnapshotId}
{planSnapshot}
on:click={() => setQueryParam(SearchParameters.SNAPSHOT_ID, `${planSnapshot.snapshot_id}`, 'PUSH')}
on:restore={() => effects.restorePlanSnapshot(planSnapshot, user)}
on:delete={() => effects.deletePlanSnapshot(planSnapshot, user)}
/>
{/each}
{#if $planSnapshotsWithSimulations.length < 1}
<div class="st-typography-label">No Plan Snapshots Found</div>
{/if}
</CardList>
</div>
</Collapse>
</fieldset>
{/if}
</div>

Expand Down
Loading
Loading