ExpandedStack.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. import Loading from "components/Loading";
  2. import Placeholder from "components/Placeholder";
  3. import TabSelector from "components/TabSelector";
  4. import TitleSection from "components/TitleSection";
  5. import React, { useContext, useEffect, useState } from "react";
  6. import { useParams } from "react-router";
  7. import api from "shared/api";
  8. import { Context } from "shared/Context";
  9. import { useRouting } from "shared/routing";
  10. import { readableDate } from "shared/string_utils";
  11. import styled from "styled-components";
  12. import ChartList from "../../chart/ChartList";
  13. import SortSelector from "../../SortSelector";
  14. import Status from "../components/Status";
  15. import {
  16. Br,
  17. InfoWrapper,
  18. LastDeployed,
  19. LineBreak,
  20. NamespaceTag,
  21. SepDot,
  22. Text,
  23. } from "../components/styles";
  24. import { getStackStatus, getStackStatusMessage } from "../shared";
  25. import { FullStackRevision, Stack, StackRevision } from "../types";
  26. import EnvGroups from "./components/EnvGroups";
  27. import RevisionList from "./_RevisionList";
  28. import SourceConfig from "./_SourceConfig";
  29. const ExpandedStack = () => {
  30. const { namespace, stack_id } = useParams<{
  31. namespace: string;
  32. stack_id: string;
  33. }>();
  34. const { pushFiltered } = useRouting();
  35. const { currentProject, currentCluster, setCurrentError } = useContext(
  36. Context
  37. );
  38. const [stack, setStack] = useState<Stack>();
  39. const [sortType, setSortType] = useState("Alphabetical");
  40. const [isLoading, setIsLoading] = useState(true);
  41. const [currentTab, setCurrentTab] = useState("apps");
  42. const [currentRevision, setCurrentRevision] = useState<FullStackRevision>();
  43. const getStack = async () => {
  44. setIsLoading(true);
  45. try {
  46. const newStack = await api
  47. .getStack<Stack>(
  48. "<token>",
  49. {},
  50. {
  51. project_id: currentProject.id,
  52. cluster_id: currentCluster.id,
  53. stack_id: stack_id,
  54. namespace,
  55. }
  56. )
  57. .then((res) => res.data);
  58. setStack(newStack);
  59. setCurrentRevision(newStack.latest_revision);
  60. setIsLoading(false);
  61. } catch (error) {
  62. setCurrentError(error);
  63. pushFiltered("/stacks", []);
  64. }
  65. };
  66. useEffect(() => {
  67. getStack();
  68. }, [stack_id]);
  69. if (isLoading) {
  70. return <Loading />;
  71. }
  72. return (
  73. <div>
  74. <StackTitleWrapper>
  75. <TitleSection
  76. materialIconClass="material-icons-outlined"
  77. icon={"lan"}
  78. capitalize
  79. >
  80. {stack.name}
  81. </TitleSection>
  82. <NamespaceTag.Wrapper>
  83. Namespace
  84. <NamespaceTag.Tag>{stack.namespace}</NamespaceTag.Tag>
  85. </NamespaceTag.Wrapper>
  86. </StackTitleWrapper>
  87. <RevisionList
  88. revisions={stack.revisions}
  89. currentRevision={currentRevision}
  90. latestRevision={stack.latest_revision}
  91. stackId={stack.id}
  92. stackNamespace={namespace}
  93. onRevisionClick={(revision) => setCurrentRevision(revision)}
  94. onRollback={() => getStack()}
  95. ></RevisionList>
  96. <Br />
  97. <InfoWrapper>
  98. <LastDeployed>
  99. <Status
  100. status={getStackStatus(stack)}
  101. message={getStackStatusMessage(stack)}
  102. />
  103. <SepDot>•</SepDot>
  104. <Text color="#aaaabb">
  105. {!stack.latest_revision?.id
  106. ? `No version found`
  107. : `v${stack.latest_revision.id}`}
  108. </Text>
  109. <SepDot>•</SepDot>
  110. Last updated {readableDate(stack.updated_at)}
  111. </LastDeployed>
  112. </InfoWrapper>
  113. {/* Stack error message */}
  114. {stack.latest_revision &&
  115. stack.latest_revision.status === "failed" &&
  116. stack.latest_revision.message?.length > 0 ? (
  117. <StackErrorMessageStyles.Wrapper>
  118. <StackErrorMessageStyles.Title color="#b7b7c9">
  119. Error reason:
  120. </StackErrorMessageStyles.Title>
  121. <StackErrorMessageStyles.Text color="#aaaabb">
  122. {stack.latest_revision.message}
  123. </StackErrorMessageStyles.Text>
  124. </StackErrorMessageStyles.Wrapper>
  125. ) : null}
  126. <TabSelector
  127. currentTab={currentTab}
  128. options={[
  129. {
  130. label: "Apps",
  131. value: "apps",
  132. component: (
  133. <>
  134. <Gap></Gap>
  135. {currentRevision.id !== stack.latest_revision.id ? (
  136. <ChartListWrapper>
  137. <Placeholder>
  138. Not available when previewing versions
  139. </Placeholder>
  140. </ChartListWrapper>
  141. ) : (
  142. <>
  143. <SortSelector
  144. setSortType={setSortType}
  145. sortType={sortType}
  146. currentView="stacks"
  147. />
  148. <ChartListWrapper>
  149. <ChartList
  150. currentCluster={currentCluster}
  151. currentView="stacks"
  152. namespace={namespace}
  153. sortType="Alphabetical"
  154. appFilters={
  155. stack?.latest_revision?.resources?.map(
  156. (res) => res.name
  157. ) || []
  158. }
  159. closeChartRedirectUrl={`${window.location.pathname}${window.location.search}`}
  160. />
  161. </ChartListWrapper>
  162. </>
  163. )}
  164. </>
  165. ),
  166. },
  167. {
  168. label: "Source Config",
  169. value: "source_config",
  170. component: (
  171. <>
  172. <SourceConfig
  173. namespace={namespace}
  174. revision={currentRevision}
  175. readOnly={stack.latest_revision.id !== currentRevision.id}
  176. onSourceConfigUpdate={() => getStack()}
  177. ></SourceConfig>
  178. </>
  179. ),
  180. },
  181. {
  182. label: "Env groups",
  183. value: "env_groups",
  184. component: (
  185. <>
  186. <Gap></Gap>
  187. <EnvGroups stack={stack} />
  188. </>
  189. ),
  190. },
  191. ]}
  192. setCurrentTab={(tab) => {
  193. setCurrentTab(tab);
  194. }}
  195. ></TabSelector>
  196. </div>
  197. );
  198. };
  199. export default ExpandedStack;
  200. const ChartListWrapper = styled.div`
  201. width: 100%;
  202. margin: auto;
  203. margin-top: 20px;
  204. padding-bottom: 125px;
  205. `;
  206. const Gap = styled.div`
  207. width: 100%;
  208. background: none;
  209. height: 30px;
  210. `;
  211. const StackErrorMessageStyles = {
  212. Text: styled(Text)`
  213. font-size: 14px;
  214. margin-bottom: 10px;
  215. `,
  216. Wrapper: styled.div`
  217. display: flex;
  218. flex-direction: column;
  219. margin-top: 5px;
  220. `,
  221. Title: styled(Text)`
  222. font-size: 16px;
  223. font-weight: bold;
  224. `,
  225. };
  226. const StackTitleWrapper = styled.div`
  227. width: 100%;
  228. display: flex;
  229. justify-content: space-between;
  230. align-items: center;
  231. // Hotfix to make sure the title section and the namespace tag are aligned
  232. ${NamespaceTag.Wrapper} {
  233. margin-bottom: 15px;
  234. }
  235. `;