1- /*
2- * This file is part of Edgehog.
3- *
4- * Copyright 2024 - 2025 SECO Mind Srl
5- *
6- * Licensed under the Apache License, Version 2.0 (the "License");
7- * you may not use this file except in compliance with the License.
8- * You may obtain a copy of the License at
9- *
10- * http://www.apache.org/licenses/LICENSE-2.0
11- *
12- * Unless required by applicable law or agreed to in writing, software
13- * distributed under the License is distributed on an "AS IS" BASIS,
14- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15- * See the License for the specific language governing permissions and
16- * limitations under the License.
17- *
18- * SPDX-License-Identifier: Apache-2.0
19- */
20-
21- import { useCallback , useMemo , useState } from "react" ;
22- import { FormattedMessage } from "react-intl" ;
23- import { graphql , usePaginationFragment } from "react-relay/hooks" ;
1+ // This file is part of Edgehog.
2+ //
3+ // Copyright 2024-2026 SECO Mind Srl
4+ //
5+ // Licensed under the Apache License, Version 2.0 (the "License");
6+ // you may not use this file except in compliance with the License.
7+ // You may obtain a copy of the License at
8+ //
9+ // http://www.apache.org/licenses/LICENSE-2.0
10+ //
11+ // Unless required by applicable law or agreed to in writing, software
12+ // distributed under the License is distributed on an "AS IS" BASIS,
13+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+ // See the License for the specific language governing permissions and
15+ // limitations under the License.
16+ //
17+ // SPDX-License-Identifier: Apache-2.0
18+
19+ import _ from "lodash" ;
20+ import { useMemo , useState } from "react" ;
2421import { Stack } from "react-bootstrap" ;
22+ import { FormattedMessage } from "react-intl" ;
23+ import { graphql , useFragment } from "react-relay/hooks" ;
2524
26- import type { ContainersTable_PaginationQuery } from "@/api/__generated__/ContainersTable_PaginationQuery.graphql" ;
2725import type {
28- ContainersTable_ContainerFragment $data,
29- ContainersTable_ContainerFragment $key,
30- } from "@/api/__generated__/ContainersTable_ContainerFragment .graphql" ;
26+ ContainersTable_ContainerEdgeFragment $data,
27+ ContainersTable_ContainerEdgeFragment $key,
28+ } from "@/api/__generated__/ContainersTable_ContainerEdgeFragment .graphql" ;
3129
32- import Form from "@/components/Form" ;
33- import StringArrayFormInput from "@/components/StringArrayFormInput" ;
34- import MonacoJsonEditor from "@/components/MonacoJsonEditor" ;
35- import MultiSelect from "./MultiSelect" ;
36- import InfiniteScroll from "./InfiniteScroll" ;
37- import DeviceMappingsFormInput from "@/components/DeviceMappingsFormInput" ;
38- import { FormRow as BaseFormRow , FormRowProps } from "@/components/FormRow" ;
39- import { restartPolicyOptions } from "@/forms/CreateRelease" ;
40- import { RECORDS_TO_LOAD_NEXT } from "@/constants" ;
4130import CollapseItem , {
4231 useCollapsibleSections ,
4332} from "@/components/CollapseItem" ;
33+ import DeviceMappingsFormInput from "@/components/DeviceMappingsFormInput" ;
34+ import Form from "@/components/Form" ;
35+ import { FormRow as BaseFormRow , FormRowProps } from "@/components/FormRow" ;
36+ import MonacoJsonEditor from "@/components/MonacoJsonEditor" ;
37+ import StringArrayFormInput from "@/components/StringArrayFormInput" ;
38+ import { restartPolicyOptions } from "@/forms/CreateRelease" ;
39+ import InfiniteScroll from "./InfiniteScroll" ;
40+ import MultiSelect from "./MultiSelect" ;
4441
4542const FormRow = ( props : FormRowProps ) => (
4643 < BaseFormRow { ...props } className = "mb-2" />
4744) ;
4845
4946/* eslint-disable relay/unused-fields */
5047const CONTAINERS_TABLE_FRAGMENT = graphql `
51- fragment ContainersTable_ContainerFragment on Release
52- @refetchable(queryName: "ContainersTable_PaginationQuery") {
53- containers(first: $first, after: $after)
54- @connection(key: "ContainersTable_containers") {
55- edges {
56- node {
57- id
58- env {
59- key
60- value
48+ fragment ContainersTable_ContainerEdgeFragment on ContainerConnection {
49+ edges {
50+ node {
51+ id
52+ env {
53+ key
54+ value
55+ }
56+ extraHosts
57+ hostname
58+ networkMode
59+ portBindings
60+ binds
61+ restartPolicy
62+ privileged
63+ memory
64+ memorySwap
65+ memoryReservation
66+ memorySwappiness
67+ cpuPeriod
68+ cpuQuota
69+ cpuRealtimePeriod
70+ cpuRealtimeRuntime
71+ tmpfs
72+ storageOpt
73+ readOnlyRootfs
74+ capAdd
75+ capDrop
76+ volumeDriver
77+ image {
78+ reference
79+ credentials {
80+ id
81+ label
82+ username
6183 }
62- extraHosts
63- hostname
64- networkMode
65- portBindings
66- binds
67- restartPolicy
68- privileged
69- memory
70- memorySwap
71- memoryReservation
72- memorySwappiness
73- cpuPeriod
74- cpuQuota
75- cpuRealtimePeriod
76- cpuRealtimeRuntime
77- tmpfs
78- storageOpt
79- readOnlyRootfs
80- capAdd
81- capDrop
82- volumeDriver
83- image {
84- reference
85- credentials {
84+ }
85+ networks {
86+ edges {
87+ node {
8688 id
89+ driver
90+ internal
8791 label
88- username
92+ options
93+ enableIpv6
8994 }
9095 }
91- networks {
92- edges {
93- node {
96+ }
97+ containerVolumes {
98+ edges {
99+ node {
100+ target
101+ volume {
94102 id
95- driver
96- internal
97103 label
104+ driver
98105 options
99- enableIpv6
100- }
101- }
102- }
103- containerVolumes {
104- edges {
105- node {
106- target
107- volume {
108- id
109- label
110- driver
111- options
112- }
113106 }
114107 }
115108 }
116- deviceMappings {
117- edges {
118- node {
119- id
120- pathInContainer
121- pathOnHost
122- cgroupPermissions
123- }
109+ }
110+ deviceMappings {
111+ edges {
112+ node {
113+ id
114+ pathInContainer
115+ pathOnHost
116+ cgroupPermissions
124117 }
125118 }
126119 }
@@ -163,7 +156,7 @@ const formatJson = (jsonString: unknown) => {
163156
164157type volumeDetailsProps = {
165158 containerVolumes : NonNullable <
166- ContainersTable_ContainerFragment $data[ "containers" ] [ "edges" ]
159+ ContainersTable_ContainerEdgeFragment $data[ "edges" ]
167160 > [ number ] [ "node" ] [ "containerVolumes" ] ;
168161 containerIndex : number ;
169162} ;
@@ -265,14 +258,14 @@ const VolumeDetails = ({
265258 ) ;
266259} ;
267260
268- type networkDetailsProps = {
261+ type NetworkDetailsProps = {
269262 networks : NonNullable <
270- ContainersTable_ContainerFragment $data[ "containers" ] [ "edges" ]
263+ ContainersTable_ContainerEdgeFragment $data[ "edges" ]
271264 > [ number ] [ "node" ] [ "networks" ] ;
272265 containerIndex : number ;
273266} ;
274267
275- const NetworkDetails = ( { networks, containerIndex } : networkDetailsProps ) => {
268+ const NetworkDetails = ( { networks, containerIndex } : NetworkDetailsProps ) => {
276269 const { toggleSection : toggleNetwork , isSectionOpen } =
277270 useCollapsibleSections < number > (
278271 networks . edges ?. map ( ( _ , index ) => index ) ?? [ ] ,
@@ -389,7 +382,7 @@ const NetworkDetails = ({ networks, containerIndex }: networkDetailsProps) => {
389382
390383type DeviceMappingDetailsProps = {
391384 deviceMappings : NonNullable <
392- ContainersTable_ContainerFragment $data[ "containers" ] [ "edges" ]
385+ ContainersTable_ContainerEdgeFragment $data[ "edges" ]
393386 > [ number ] [ "node" ] [ "deviceMappings" ] ;
394387 containerIndex ?: number ;
395388} ;
@@ -433,7 +426,7 @@ const DeviceMappingDetails = ({
433426} ;
434427
435428type ContainerRecord = NonNullable <
436- ContainersTable_ContainerFragment $data[ "containers" ] [ "edges" ]
429+ ContainersTable_ContainerEdgeFragment $data[ "edges" ]
437430> [ number ] [ "node" ] ;
438431type ContainerEnv = ContainerRecord [ "env" ] ;
439432
@@ -929,25 +922,24 @@ const ContainerDetails = ({ container, index }: ContainerDetailsProps) => {
929922
930923type ContainersTableProps = {
931924 className ?: string ;
932- containersRef : ContainersTable_ContainerFragment$key ;
925+ containersRef : ContainersTable_ContainerEdgeFragment$key ;
926+ loading ?: boolean ;
927+ onLoadMore ?: ( ) => void ;
933928} ;
934929
935930const ContainersTable = ( {
936931 className,
937932 containersRef,
933+ loading = false ,
934+ onLoadMore,
938935} : ContainersTableProps ) => {
939- const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment <
940- ContainersTable_PaginationQuery ,
941- ContainersTable_ContainerFragment$key
942- > ( CONTAINERS_TABLE_FRAGMENT , containersRef ) ;
943-
944- const loadNextContainers = useCallback ( ( ) => {
945- if ( hasNext && ! isLoadingNext ) loadNext ( RECORDS_TO_LOAD_NEXT ) ;
946- } , [ hasNext , isLoadingNext , loadNext ] ) ;
947-
948- const containers : ContainerRecord [ ] = useMemo ( ( ) => {
949- return data . containers ?. edges ?. map ( ( edge ) => edge ?. node ) ?? [ ] ;
950- } , [ data ] ) ;
936+ const containersFragment = useFragment (
937+ CONTAINERS_TABLE_FRAGMENT ,
938+ containersRef || null ,
939+ ) ;
940+ const containers = useMemo < ContainerRecord [ ] > ( ( ) => {
941+ return _ . compact ( containersFragment ?. edges ?. map ( ( e ) => e ?. node ) ) ?? [ ] ;
942+ } , [ containersFragment ] ) ;
951943
952944 const { toggleSection : toggleIndex , isSectionOpen } =
953945 useCollapsibleSections < number > ( containers . map ( ( _ , index ) => index ) ) ;
@@ -971,8 +963,8 @@ const ContainersTable = ({
971963 < InfiniteScroll
972964 key = { container . id }
973965 className = { className }
974- loading = { isLoadingNext }
975- onLoadMore = { hasNext ? loadNextContainers : undefined }
966+ loading = { loading }
967+ onLoadMore = { onLoadMore }
976968 >
977969 < CollapseItem
978970 type = "card-parent"
0 commit comments