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.

147 lines
5.1 KiB

2 years ago
  1. import Page, {PageBody, PageFlexColumn, PageFlexRow} from "../primitives/page/Page";
  2. import {useNavigate, useParams} from "react-router";
  3. import React, {useCallback, useContext, useEffect, useMemo, useState} from "react";
  4. import ProgramContext from "../contexts/ProgramContext";
  5. import LoadingPage from "./LoadingPage";
  6. import Header, {HeaderButton, HeaderTitle} from "../primitives/header/Header";
  7. import {Icon} from "../primitives/Shared";
  8. import {
  9. faArrowUpRightDots, faCheck,
  10. faChevronLeft,
  11. faClose,
  12. faLevelUpAlt,
  13. faPlus,
  14. faStopwatch
  15. } from "@fortawesome/free-solid-svg-icons";
  16. import {Size, stringToValues, valuesToString} from "../models/Shared";
  17. import {TitleLine} from "../primitives/misc/Misc";
  18. import Blob, {BlobInput, BlobText} from "../primitives/blob/Blob";
  19. import {ProgramStep} from "../models/Programs";
  20. import programRepo from "../actions/programs";
  21. interface StepOption {
  22. level: number
  23. duration: string
  24. }
  25. export default function EditProgramPage() {
  26. const {programs, refreshPrograms} = useContext(ProgramContext);
  27. const navigate = useNavigate();
  28. const {id} = useParams();
  29. const program = useMemo(() => programs?.find(p => p.id === id), [programs, id]);
  30. const [name, setName] = useState(program?.name || "");
  31. const [steps, setSteps] = useState<StepOption[]>([]);
  32. const [wait, setWait] = useState<boolean>(false);
  33. useEffect(() => {
  34. if (program) {
  35. setName(program.name)
  36. setSteps(program.steps.map(s => ({
  37. level: s.values.level || 0,
  38. duration: valuesToString(s.duration || {}),
  39. })));
  40. }
  41. }, [program]);
  42. const onSave = useCallback(() => {
  43. const id = program?.id || undefined;
  44. const newSteps: ProgramStep[] = steps.map(s => ({
  45. values: {level: s.level},
  46. duration: stringToValues(s.duration),
  47. }));
  48. setWait(true);
  49. programRepo().save({id, name, steps: newSteps})
  50. .then(res => {
  51. if (res) {
  52. navigate(program ? `/programs/${program.id}` : "/");
  53. refreshPrograms();
  54. } else {
  55. setWait(false);
  56. }
  57. });
  58. }, [program, name, steps, navigate, refreshPrograms]);
  59. if (programs === null) {
  60. return <LoadingPage text="Henter programmer"/>;
  61. } else if (wait) {
  62. return <LoadingPage text="Lagrer programm"/>;
  63. }
  64. const title = program ? `Endre "${program.name}"` : "Nytt programm";
  65. const canSave = name.trim() !== "" && steps.length > 0 && !steps.find(p => p.level === 0);
  66. return (
  67. <Page title={title}>
  68. <Header>
  69. <HeaderButton onClick={() => navigate(program ? `/programs/${program.id}` : "/")}>
  70. <Icon value={faChevronLeft}/>
  71. </HeaderButton>
  72. <HeaderTitle>{title}</HeaderTitle>
  73. </Header>
  74. <PageBody>
  75. <PageFlexRow collapseOn={Size.Tablet}>
  76. <PageFlexColumn flex={1}>
  77. <TitleLine>Programm</TitleLine>
  78. <Blob fillOn={Size.Any}>
  79. <BlobText>Navn</BlobText>
  80. <BlobInput type="text" value={name} onChange={setName} flex={1}/>
  81. </Blob>
  82. <Blob color={canSave ? "indigo" : "gray"} onClick={onSave} disabled={!canSave}>
  83. <BlobText>
  84. <Icon value={faCheck}/> Lagre
  85. </BlobText>
  86. </Blob>
  87. </PageFlexColumn>
  88. <PageFlexColumn flex={1}>
  89. <TitleLine>Steg</TitleLine>
  90. {steps.map((s, i) => {
  91. const onChange = (arg: Partial<StepOption>) => setSteps(prev => {
  92. return prev.map((ps, pi) => (pi === i ? {...ps, ...arg} : ps));
  93. });
  94. const onRemove = () => setSteps(prev => {
  95. return prev.filter((ignored, pi) => pi !== i);
  96. })
  97. return (
  98. <PageFlexRow key={i}>
  99. <Blob>
  100. <BlobText>
  101. <Icon value={faArrowUpRightDots}/>
  102. </BlobText>
  103. <BlobInput
  104. type="number" value={s.level}
  105. onChange={level => onChange({level})}
  106. />
  107. </Blob>
  108. <Blob flex={2}>
  109. <BlobText>
  110. <Icon value={faStopwatch}/>
  111. </BlobText>
  112. <BlobInput
  113. flex={1} type="text" value={s.duration} placeholder="Manuell"
  114. onChange={duration => onChange({duration})}
  115. />
  116. </Blob>
  117. <Blob color="red" onClick={onRemove}>
  118. <BlobText>
  119. <Icon value={faClose}/>
  120. </BlobText>
  121. </Blob>
  122. </PageFlexRow>
  123. );
  124. })}
  125. <Blob color="green" onClick={() => setSteps(prev => [...prev, {duration: "", level: 1}])}>
  126. <BlobText>
  127. <Icon value={faPlus}/> Legg til
  128. </BlobText>
  129. </Blob>
  130. </PageFlexColumn>
  131. </PageFlexRow>
  132. </PageBody>
  133. </Page>
  134. );
  135. }