Loggest thine Stuff
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

171 lines
5.5 KiB

  1. <script lang="ts">
  2. import { getStores } from "$app/stores";
  3. import { sl3 } from "$lib/clients/sl3";
  4. import Modal from "$lib/components/common/Modal.svelte";
  5. import ModalBody from "$lib/components/common/ModalBody.svelte";
  6. import { getModalContext } from "$lib/components/contexts/ModalContext.svelte";
  7. import { getScopeContext } from "$lib/components/contexts/ScopeContext.svelte";
  8. import { getSprintListContext } from "$lib/components/contexts/SprintListContext.svelte";
  9. import PartInput from "$lib/components/controls/PartInput.svelte";
  10. import SprintKindSelect from "$lib/components/controls/SprintKindSelect.svelte";
  11. import TimeRangeInput from "$lib/components/controls/TimeRangeInput.svelte";
  12. import Checkbox from "$lib/components/layout/Checkbox.svelte";
  13. import type Scope from "$lib/models/scope";
  14. import type Sprint from "$lib/models/sprint";
  15. import { SprintKind, type SprintInput, type SprintInputPart } from "$lib/models/sprint";
  16. import { formatFormTime } from "$lib/utils/date";
  17. import { partsDiff } from "$lib/utils/sprint";
  18. const {currentModal, closeModal} = getModalContext();
  19. const {scope} = getScopeContext();
  20. const {reloadSprintList} = getSprintListContext();
  21. const {page} = getStores();
  22. let sprint: SprintInput
  23. let sprintId: number
  24. let scopeId: number
  25. let oldParts: SprintInputPart[]
  26. let intervalName: string;
  27. let openedDate: Date
  28. let op: string
  29. let error: string
  30. let loading: boolean
  31. let show: boolean
  32. $: switch ($currentModal.name) {
  33. case "sprint.create":
  34. initCreate($scope)
  35. break;
  36. case "sprint.edit":
  37. initEdit($currentModal.sprint)
  38. break;
  39. default:
  40. loading = false;
  41. error = null;
  42. show = false;
  43. }
  44. function initCreate(scope: Scope) {
  45. sprint = {
  46. name: "",
  47. description: "",
  48. fromTime: "",
  49. toTime: "",
  50. kind: SprintKind.Stats,
  51. aggregateName: "",
  52. aggregateRequired: 0,
  53. isCoarse: false,
  54. isTimed: false,
  55. isUnweighted: false,
  56. parts: [],
  57. }
  58. intervalName = "this_month";
  59. scopeId = scope.id;
  60. op = "Create"
  61. openedDate = new Date();
  62. show = true;
  63. }
  64. function initEdit(current: Sprint) {
  65. sprint = {
  66. name: current.name,
  67. description: current.description,
  68. fromTime: formatFormTime(current.fromTime),
  69. toTime: formatFormTime(current.toTime),
  70. kind: current.kind,
  71. aggregateName: current.aggregateName,
  72. aggregateRequired: current.aggregateRequired,
  73. isCoarse: current.isCoarse,
  74. isTimed: current.isTimed,
  75. isUnweighted: current.isUnweighted,
  76. };
  77. sprintId = current.id;
  78. scopeId = current.scopeId;
  79. intervalName = "specific_dates";
  80. if (sprint.kind === SprintKind.Stats) {
  81. sprint.parts = current.progress.map(p => ({partId: p.id, required: p.required}));
  82. } else {
  83. sprint.parts = current.partIds.map(p => ({partId: p, required: 0}));
  84. }
  85. oldParts = [...sprint.parts];
  86. op = "Edit"
  87. openedDate = new Date();
  88. show = true;
  89. }
  90. async function submit() {
  91. error = null;
  92. loading = true;
  93. const submission: SprintInput = {
  94. ...sprint,
  95. fromTime: new Date(sprint.fromTime).toISOString(),
  96. toTime: new Date(sprint.toTime).toISOString(),
  97. }
  98. try {
  99. switch (op) {
  100. case "Create":
  101. await sl3(fetch, $page.stuff.idToken).createSprint(scopeId, submission);
  102. break;
  103. case "Edit":
  104. await sl3(fetch, $page.stuff.idToken).updateSprint(scopeId, sprintId, submission);
  105. const {added, removed} = partsDiff(oldParts, submission.parts)
  106. for (const part of added) {
  107. await sl3(fetch, $page.stuff.idToken).upsertSprintPart(scopeId, sprintId, part).catch(() => {});
  108. }
  109. for (const part of removed) {
  110. await sl3(fetch, $page.stuff.idToken).deleteSprintPart(scopeId, sprintId, part).catch(() => {});
  111. }
  112. break;
  113. }
  114. await reloadSprintList();
  115. closeModal();
  116. } catch(err) {
  117. if (err.statusCode != null) {
  118. error = err.statusMessage;
  119. } else {
  120. error = err
  121. }
  122. } finally {
  123. loading = false;
  124. }
  125. }
  126. </script>
  127. <form on:submit|preventDefault={submit}>
  128. <Modal wide closable show={show} verb={op} noun="sprint" disabled={loading} error={error}>
  129. <ModalBody>
  130. <label for="name">Name</label>
  131. <input name="name" type="text" bind:value={sprint.name} />
  132. <label for="description">Description</label>
  133. <textarea name="description" bind:value={sprint.description} />
  134. <label for="time">Time</label>
  135. <TimeRangeInput openDate={openedDate} bind:from={sprint.fromTime} bind:to={sprint.toTime} bind:intervalName={intervalName} />
  136. <label for="aggregateName">Aggregate Name</label>
  137. <input name="aggregateName" type="text" bind:value={sprint.aggregateName} />
  138. {#if sprint.kind != SprintKind.Items}
  139. <label for="aggregateValue">Aggregate Goal</label>
  140. <input name="aggregateValue" type="number" bind:value={sprint.aggregateRequired} />
  141. {/if}
  142. <Checkbox bind:checked={sprint.isTimed} label="Sprint is timed" />
  143. <Checkbox bind:checked={sprint.isCoarse} label="Show only aggregate" />
  144. <Checkbox bind:checked={sprint.isUnweighted} label="Aggregate is unweighted" />
  145. </ModalBody>
  146. <ModalBody>
  147. <label for="kind">Kind</label>
  148. <SprintKindSelect disabled={op === "Edit"} bind:kind={sprint.kind} />
  149. <label for="parts">Parts</label>
  150. <PartInput bind:value={sprint.parts} kind={sprint.kind} scopeId={scopeId} />
  151. </ModalBody>
  152. </Modal>
  153. </form>