Plan stuff. Log 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.

202 lines
5.1 KiB

4 years ago
  1. import { writable, derived } from "svelte/store";
  2. import slApi from "../api/stufflog";
  3. import Activity, { ActivityUpdate } from "../models/activity";
  4. import Period, { PeriodUpdate } from "../models/period";
  5. const UNKNOWN_GOAL = Object.freeze({name: "(Unknown)", subGoals: []});
  6. const NO_SUB_GOAL = Object.freeze({name: "(None)"});
  7. const UNKNOWN_SUB_GOAL = Object.freeze({name: "(Unknown)"});
  8. const UNKNOWN_ACTIVITY = Object.freeze({name: "(Unknown)", subActivities: []});
  9. const UNKNOWN_SUB_ACTIVITY = Object.freeze({name: "(Unknown)", unitName: "unit"});
  10. function calculateTotalScore(period) {
  11. return period.goals.reduce((o, g) => o = ({...o, [g.id]: (
  12. period.logs.filter(l => l.goalId === g.id)
  13. .map(l => l.score.total)
  14. .reduce((n, m) => n + m, 0)
  15. )}), {})
  16. }
  17. function generateLogTable(activities, period) {
  18. const newTable = [];
  19. for (const log of period.logs) {
  20. const goal = period.goals.find(g => g.id === log.goalId) || UNKNOWN_GOAL;
  21. const subGoal = log.subGoalId
  22. ? goal.subGoals.find(s => s.id === log.subGoalId) || UNKNOWN_SUB_GOAL
  23. : NO_SUB_GOAL;
  24. const activity = activities.find(a => a.id === goal.activityId) || UNKNOWN_ACTIVITY;
  25. const subActivity = activity.subActivities.find(s => s.id === log.subActivityId) || UNKNOWN_SUB_ACTIVITY;
  26. newTable.push({log, goal, subGoal, activity, subActivity})
  27. }
  28. newTable.sort((a, b) => a.log.date - b.log.date);
  29. return newTable;
  30. }
  31. function replaceActivity(d, activity) {
  32. const data = {
  33. ...d,
  34. activities: [
  35. ...d.activities.filter(a => a.id !== activity.id),
  36. activity,
  37. ].sort((a,b) => a.name.localeCompare(b.name)),
  38. };
  39. data.periods = data.periods.map(p => ({
  40. ...p,
  41. scores: calculateTotalScore(p),
  42. table: generateLogTable(data.activities, p),
  43. }));
  44. return data;
  45. }
  46. function replacePeriod(d, period) {
  47. return {
  48. ...d,
  49. periods: [
  50. ...d.periods.filter(a => a.id !== period.id),
  51. {
  52. ...period,
  53. scores: calculateTotalScore(period),
  54. table: generateLogTable(d.activities, period),
  55. },
  56. ].sort((a,b) => b.from - a.from),
  57. };
  58. }
  59. function deleteActivity(d, id) {
  60. return {
  61. ...d,
  62. activities: d.activities.filter(a => a.id !== id),
  63. };
  64. }
  65. function deletePeriod(d, id) {
  66. return {
  67. ...d,
  68. periods: d.periods.filter(a => a.id !== id),
  69. };
  70. }
  71. function createStufflogStore() {
  72. /** @type {{activities: Activity[], periods: Period[], logTables: {}}} */
  73. const initialData = {activities: [], periods: [], logTables: {}};
  74. const {set, update, subscribe} = writable(initialData);
  75. return {
  76. subscribe,
  77. async listActivities() {
  78. const activities = await slApi.listActivities();
  79. update(d => ({
  80. ...d,
  81. activities: activities.sort((a,b) => a.name.localeCompare(b.name)),
  82. periods: d.periods.map(p => ({
  83. ...p,
  84. scores: calculateTotalScore(p),
  85. table: generateLogTable(activities, p),
  86. })),
  87. }));
  88. },
  89. async loadActivity(id) {
  90. const activity = await slApi.findActivity(id);
  91. update(d => replaceActivity(d, activity));
  92. },
  93. async listPeriods() {
  94. const periods = await slApi.listPeriods();
  95. update(d => ({
  96. ...d,
  97. periods: periods.sort((a,b) => b.from - a.from).map(p => ({
  98. ...p,
  99. scores: calculateTotalScore(p),
  100. table: generateLogTable(d.activities, p),
  101. })),
  102. }));
  103. },
  104. async loadPeriods(id) {
  105. const period = await slApi.findPeriod(id);
  106. update(d => replacePeriod(d, period));
  107. },
  108. /**
  109. * Create period
  110. *
  111. * @param {Period} input
  112. */
  113. async createPeriod(input) {
  114. const period = await slApi.postPeriod(input);
  115. update(d => replacePeriod(d, period));
  116. },
  117. /**
  118. * Create an activity
  119. *
  120. * @param {Activity} input
  121. */
  122. async createActivity(input) {
  123. const activity = await slApi.postActivity(input);
  124. update(d => replaceActivity(d, activity))
  125. },
  126. /**
  127. * Update activities
  128. *
  129. * @param {string} id
  130. * @param {...ActivityUpdate} updates
  131. */
  132. async updateActivity(id, ...updates) {
  133. const activity = await slApi.patchActivity(id, ...updates);
  134. update(d => replaceActivity(d, activity));
  135. },
  136. /**
  137. * Update periods
  138. *
  139. * @param {string} id
  140. * @param {...PeriodUpdate} updates
  141. */
  142. async updatePeriod(id, ...updates) {
  143. const period = await slApi.patchPeriod(id, ...updates);
  144. update(d => replacePeriod(d, period));
  145. },
  146. /**
  147. * Delete an activity
  148. *
  149. * @param {string} id
  150. */
  151. async deleteActivity(id) {
  152. const activity = await slApi.deleteActivity(id);
  153. update(d => deleteActivity(d, activity.id));
  154. },
  155. /**
  156. * Delete a period
  157. *
  158. * @param {string} id
  159. */
  160. async deletePeriod(id) {
  161. const period = await slApi.deletePeriod(id);
  162. update(d => deletePeriod(d, period.id));
  163. },
  164. /**
  165. * Clear all data. This does not do any API call.
  166. */
  167. clearAll() {
  168. set({activities: [], periods: []});
  169. },
  170. }
  171. };
  172. const stufflog = createStufflogStore();
  173. export default stufflog;