• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CSCfi / metadata-submitter-frontend / 17910605241

22 Sep 2025 09:17AM UTC coverage: 58.389% (-0.01%) from 58.4%
17910605241

push

github

Hang Le
Rename folder to bucket (merge commit)

Merge branch 'feature/datafolder-to-bucket' into 'main'
* Change folder icon to bucket icon

* Change folder to bucket, subfolder to folder in variables, UI language, tests

Closes #1096
See merge request https://gitlab.ci.csc.fi/sds-dev/sd-submit/metadata-submitter-frontend/-/merge_requests/1174

Reviewed-by: Liisa Lado-Villar <145-lilado@users.noreply.gitlab.ci.csc.fi>
Approved-by: Liisa Lado-Villar <145-lilado@users.noreply.gitlab.ci.csc.fi>
Approved-by: Hang Le <lhang@csc.fi>
Co-authored-by: Monika Radaviciute <mradavic@csc.fi>
Merged by Hang Le <lhang@csc.fi>

595 of 804 branches covered (74.0%)

Branch coverage included in aggregate %.

37 of 95 new or added lines in 12 files covered. (38.95%)

3 existing lines in 3 files now uncovered.

5349 of 9376 relevant lines covered (57.05%)

5.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

12.61
/src/components/SubmissionWizard/WizardComponents/WizardDataBucketTable.tsx
1
import React, { useState, useEffect } from "react"
1✔
2

3
import Box from "@mui/material/Box"
1✔
4
import Radio from "@mui/material/Radio"
1✔
5
import SvgIcon from "@mui/material/SvgIcon"
1✔
6
import Typography from "@mui/material/Typography"
1✔
7
import { GridColDef } from "@mui/x-data-grid"
8
import type { GridSortDirection } from "@mui/x-data-grid"
9
import { upperFirst } from "lodash"
1✔
10
import { useTranslation } from "react-i18next"
1✔
11

12
import { files } from "../../../../playwright/fixtures/files_response" // MOCK files array
1✔
13

14
import DataTable from "components/DataTable"
1✔
15
import { ResponseStatus } from "constants/responseStatus"
1✔
16
import { updateStatus } from "features/statusMessageSlice"
1✔
17
import { useAppSelector, useAppDispatch } from "hooks"
1✔
18
import filesAPIService from "services/filesAPI"
1✔
19
import type { DataBucketRow } from "types"
20

21
type DataBucketTableProps = {
22
  selectedBucket: string
23
  bucket: string
24
  handleBucketChange: (e: React.ChangeEvent<HTMLInputElement>) => void
25
  handleFilesView: (bucketName: string) => void
26
}
27

28
/*
29
 * Render a table of shared buckets received from SD Connect
30
 */
31
const WizardDataBucketTable: React.FC<DataBucketTableProps> = props => {
1✔
NEW
32
  const { selectedBucket, bucket, handleBucketChange, handleFilesView } = props
×
33
  const projectId = useAppSelector(state => state.projectId)
×
34
  const dispatch = useAppDispatch()
×
35

36
  const { t } = useTranslation()
×
37

38
  const columns: GridColDef[] = [
×
39
    {
×
40
      field: "name",
×
41
      headerName: t("dataTable.name"),
×
42
      sortable: true,
×
43
      renderCell: params => {
×
44
        return (
×
45
          <Box display="flex" alignItems="center" height="100%">
×
NEW
46
            {!bucket && (
×
47
              <Radio
×
NEW
48
                checked={selectedBucket === params.row.name}
×
NEW
49
                onChange={handleBucketChange}
×
50
                value={params.row.name}
×
51
                name="radio-buttons"
×
52
                inputProps={{ "aria-label": params.row.name }}
×
53
              />
54
            )}
55
            {/* Use mdiPail path: MUI icons lack a corresponding icon */}
NEW
56
            <SvgIcon
×
57
              color="primary"
×
58
              fontSize="medium"
×
59
              sx={{ ml: "1rem", mr: "0.5rem" }}
×
60
              onClick={() => handleFilesView(params.row.name)}
×
61
            >
NEW
62
              <path d="M11.5 7.63C11.97 7.35 12.58 7.5 12.86 8C13.14 8.47 12.97 9.09 12.5 9.36L4.27 14.11C3.79 14.39 3.18 14.23 2.9 13.75C2.62 13.27 2.79 12.66 3.27 12.38L11.5 7.63M7 21L5.79 14.97L13.21 10.69C14 10.26 14.5 9.44 14.5 8.5C14.5 7.12 13.38 6 12 6C11.53 6 11.09 6.13 10.71 6.36L4.76 9.79L4 6H3V4H21V6H20L17 21H7Z" />
×
NEW
63
            </SvgIcon>
×
64
            <Typography component="span" onClick={() => handleFilesView(params.row.name)}>
×
65
              {upperFirst(params.row.name)}
×
66
            </Typography>
×
67
          </Box>
×
68
        )
69
      },
×
70
    },
×
71
    {
×
72
      field: "items",
×
73
      headerName: t("dataTable.totalItems"),
×
74
      type: "number",
×
75
    },
×
76
    {
×
77
      field: "size",
×
78
      headerName: t("dataTable.size"),
×
79
      sortable: true, // TODO: need to convert sizes to humanreadable bytes
×
80
    },
×
81
  ]
82

83
  useEffect(() => {
×
84
    let isMounted = true
×
85
    const getFiles = async () => {
×
86
      if (isMounted) {
×
87
        try {
×
88
          const response = await filesAPIService.getProjectFiles(projectId)
×
89
          const files = response.data
×
90
          sessionStorage.setItem("files", JSON.stringify(files))
×
91
          // TODO: consider saving files in redux instead of sessionStorage if needed
92
        } catch (error) {
×
93
          dispatch(
×
94
            updateStatus({
×
95
              status: ResponseStatus.error,
×
96
              response: error,
×
97
              helperText: "",
×
98
            })
×
99
          )
×
100
        }
×
101
      }
×
102
    }
×
103
    getFiles()
×
104
    return () => {
×
105
      isMounted = false
×
106
    }
×
107
  }, [])
×
108

109
  useEffect(() => {
×
NEW
110
    !bucket ? setTotalItems(getBucketNames().length) : setTotalItems(1)
×
NEW
111
  }, [bucket])
×
112

NEW
113
  const getRows = (): DataBucketRow[] => {
×
NEW
114
    const bucketNames = getBucketNames()
×
NEW
115
    return bucketNames
×
NEW
116
      .filter(bucketName => (!!bucket ? bucketName === bucket : bucketName))
×
NEW
117
      .map(bucketName => {
×
NEW
118
        const currentFiles = files.filter(file => file.path.includes(`/${bucketName}/`))
×
119
        const totalSize = currentFiles.reduce((acc, currentFile) => acc + currentFile["bytes"], 0)
×
120
        return {
×
NEW
121
          id: bucketName,
×
NEW
122
          name: bucketName,
×
123
          size: totalSize,
×
124
          items: currentFiles.length,
×
125
        }
×
126
      })
×
127
  }
×
128

NEW
129
  const getBucketNames = () => [...new Set(files.map(file => file["path"].split("/")[1]))]
×
130

131
  const sortingModel = [
×
132
    {
×
133
      field: "name",
×
134
      sort: "asc" as GridSortDirection,
×
135
    },
×
136
  ]
137

138
  const [page, setPage] = useState<number>(0)
×
139
  const [totalItems, setTotalItems] = useState<number>(0)
×
140

141
  const fetchPageOnChange = (page: number) => {
×
142
    setPage(page)
×
143
  }
×
144

145
  return (
×
146
    <DataTable
×
147
      rows={getRows()}
×
148
      columns={columns}
×
149
      page={page}
×
150
      sortingModel={sortingModel}
×
151
      totalItems={totalItems}
×
152
      fetchPageOnChange={fetchPageOnChange}
×
153
    />
154
  )
155
}
×
156

157
export default WizardDataBucketTable
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc