ExpandedStack.tsx 6.7 KB

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