show.tsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import React, { useEffect, useState } from 'react';
  2. import { Show } from "@refinedev/antd";
  3. import { useShow, useMany } from "@refinedev/core";
  4. import { Card, Descriptions, Space, Tag, Typography, Table, Button, Tooltip } from 'antd';
  5. import { RefreshButton, DateField } from '@refinedev/antd';
  6. import { ApiOutlined, LinkOutlined, InfoCircleOutlined } from '@ant-design/icons';
  7. const { Title, Text } = Typography;
  8. export const BlueprintShow = () => {
  9. const { queryResult } = useShow();
  10. const { data, isLoading } = queryResult;
  11. const record = data?.data;
  12. // Extract component IDs from the blueprint config
  13. const componentIds = record?.config?.components?.map((component: {id: string}) => component.id) || [];
  14. // Fetch component details for all component IDs
  15. const { data: componentsData, isLoading: componentsLoading } = useMany({
  16. resource: "components",
  17. ids: componentIds,
  18. queryOptions: {
  19. enabled: componentIds.length > 0,
  20. },
  21. });
  22. return (
  23. <Show
  24. isLoading={isLoading}
  25. headerButtons={[
  26. <Button
  27. key="deploy"
  28. type="primary"
  29. icon={<ApiOutlined />}
  30. href={`/deployments/create?blueprintId=${record?.id}`}
  31. >
  32. Deploy
  33. </Button>,
  34. <RefreshButton key="refresh" />
  35. ]}
  36. >
  37. <Title level={4}>Blueprint Details</Title>
  38. <Card style={{ marginBottom: 20 }}>
  39. <Descriptions bordered column={2}>
  40. <Descriptions.Item label="ID">{record?.id}</Descriptions.Item>
  41. <Descriptions.Item label="Name">{record?.name}</Descriptions.Item>
  42. <Descriptions.Item label="Version">{record?.version}</Descriptions.Item>
  43. <Descriptions.Item label="Components">
  44. <Tag color="blue">{record?.config?.components?.length || 0} Components</Tag>
  45. </Descriptions.Item>
  46. <Descriptions.Item label="Network Policies">
  47. <Tag color="purple">{record?.config?.networkPolicies?.length || 0} Policies</Tag>
  48. </Descriptions.Item>
  49. <Descriptions.Item label="Description" span={2}>
  50. {record?.description}
  51. </Descriptions.Item>
  52. </Descriptions>
  53. </Card>
  54. <Card title="Components" style={{ marginBottom: 20 }}>
  55. {record?.config?.components &&
  56. <Table
  57. dataSource={record.config.components.map((component: any) => {
  58. // Find the full component details from the components data
  59. const componentDetails = componentsData?.data?.find((c: any) => c.id === component.id);
  60. return {
  61. ...component,
  62. componentDetails,
  63. };
  64. })}
  65. rowKey="id"
  66. loading={componentsLoading}
  67. pagination={false}
  68. expandable={{
  69. expandedRowRender: (record: any) => (
  70. <Card type="inner" title="Component Details">
  71. <Descriptions bordered column={2} size="small">
  72. <Descriptions.Item label="Description">{record.componentDetails?.description}</Descriptions.Item>
  73. <Descriptions.Item label="Language">{record.componentDetails?.language}</Descriptions.Item>
  74. <Descriptions.Item label="Type">{record.componentDetails?.type}</Descriptions.Item>
  75. <Descriptions.Item label="Version">{record.componentDetails?.version}</Descriptions.Item>
  76. </Descriptions>
  77. </Card>
  78. ),
  79. }}
  80. >
  81. <Table.Column title="ID" dataIndex="id" />
  82. <Table.Column
  83. title="Name"
  84. dataIndex="name"
  85. render={(value, record: any) => (
  86. <Space>
  87. {value}
  88. {record.componentDetails && (
  89. <Tooltip title="View Component Details">
  90. <Button
  91. type="link"
  92. icon={<InfoCircleOutlined />}
  93. href={`/components/show/${record.id}`}
  94. size="small"
  95. />
  96. </Tooltip>
  97. )}
  98. </Space>
  99. )}
  100. />
  101. <Table.Column
  102. title="Component Type"
  103. render={(_, record: any) => (
  104. <Tag color="blue">{record.componentDetails?.type || "unknown"}</Tag>
  105. )}
  106. />
  107. <Table.Column
  108. title="Public Access"
  109. dataIndex="publicAccess"
  110. render={(value) => value ? <Tag color="green">Yes</Tag> : <Tag color="red">No</Tag>}
  111. />
  112. <Table.Column
  113. title="Resources"
  114. dataIndex="resources"
  115. render={(resources) => (
  116. <>
  117. <div>CPU: {resources.cpu}</div>
  118. <div>Memory: {resources.memory}</div>
  119. <div>Storage: {resources.storage}</div>
  120. </>
  121. )}
  122. />
  123. <Table.Column
  124. title="Autoscaling"
  125. dataIndex="autoscaling"
  126. render={(autoscaling) => autoscaling?.enabled ? (
  127. <>
  128. <div>Min: {autoscaling.minReplicas}</div>
  129. <div>Max: {autoscaling.maxReplicas}</div>
  130. <div>CPU: {autoscaling.cpuThreshold}%</div>
  131. </>
  132. ) : <Tag color="red">Disabled</Tag>}
  133. />
  134. </Table>
  135. }
  136. </Card>
  137. <Card title="Network Policies" style={{ marginBottom: 20 }}>
  138. {record?.config?.networkPolicies &&
  139. <Table
  140. dataSource={record.config.networkPolicies}
  141. rowKey="name"
  142. pagination={false}
  143. >
  144. <Table.Column title="Name" dataIndex="name" />
  145. <Table.Column
  146. title="Sources"
  147. dataIndex="sourceComponents"
  148. render={(sources) => sources?.map((src: string) => <Tag key={src}>{src}</Tag>)}
  149. />
  150. <Table.Column
  151. title="Targets"
  152. dataIndex="targetComponents"
  153. render={(targets) => targets?.map((target: string) => <Tag key={target}>{target}</Tag>)}
  154. />
  155. <Table.Column
  156. title="Ports"
  157. dataIndex="ports"
  158. render={(ports) => ports?.map((port: number) => <Tag key={port}>{port}</Tag>)}
  159. />
  160. <Table.Column
  161. title="Action"
  162. dataIndex="action"
  163. render={(action) => action === 'allow' ?
  164. <Tag color="green">Allow</Tag> :
  165. <Tag color="red">Deny</Tag>
  166. }
  167. />
  168. </Table>
  169. }
  170. </Card>
  171. <Card title="Environment Variables" style={{ marginBottom: 20 }}>
  172. {record?.config?.envVariables &&
  173. <Table
  174. dataSource={Object.entries(record.config.envVariables).map(([key, value]) => ({ key, value }))}
  175. rowKey="key"
  176. pagination={false}
  177. >
  178. <Table.Column title="Key" dataIndex="key" />
  179. <Table.Column title="Value" dataIndex="value" />
  180. </Table>
  181. }
  182. {(!record?.config?.envVariables || Object.keys(record.config.envVariables).length === 0) &&
  183. <Text type="secondary">No environment variables defined</Text>
  184. }
  185. </Card>
  186. <Card title="Secrets" style={{ marginBottom: 20 }}>
  187. {record?.config?.secrets &&
  188. <Table
  189. dataSource={record.config.secrets}
  190. rowKey="name"
  191. pagination={false}
  192. >
  193. <Table.Column title="Name" dataIndex="name" />
  194. <Table.Column title="Type" dataIndex="type" />
  195. <Table.Column title="Mount Path" dataIndex="mountPath" />
  196. </Table>
  197. }
  198. {(!record?.config?.secrets || record.config.secrets.length === 0) &&
  199. <Text type="secondary">No secrets defined</Text>
  200. }
  201. </Card>
  202. <Card title="Metadata">
  203. <Descriptions bordered column={2}>
  204. <Descriptions.Item label="Created At">
  205. <DateField value={record?.createdAt} format="LLL" />
  206. </Descriptions.Item>
  207. <Descriptions.Item label="Updated At">
  208. <DateField value={record?.updatedAt} format="LLL" />
  209. </Descriptions.Item>
  210. <Descriptions.Item label="Created By">{record?.createdBy}</Descriptions.Item>
  211. </Descriptions>
  212. </Card>
  213. </Show>
  214. );
  215. };