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

CSCfi / metadata-submitter-frontend / 20261176525

16 Dec 2025 08:20AM UTC coverage: 58.415% (-0.7%) from 59.151%
20261176525

push

github

Liisa Lado-Villar
Add Dockerfile and nginx config (merge commit)

Merge branch 'feature/dockerfile-for-production' into 'main'
* make nginx.conf more compact

* fix nginx.conf

* refactor nginx config to minimal and to override default

* fix Dockerfile

* Resolve the file paths for production build in Docker container

* add Dockerfile and nginx config

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

Approved-by: Joonatan Mäkinen <jmakine@csc.fi>
Co-authored-by: lhang <lhang@csc.fi>
Merged by Liisa Lado-Villar <145-lilado@users.noreply.gitlab.ci.csc.fi>

636 of 829 branches covered (76.72%)

Branch coverage included in aggregate %.

4 of 38 new or added lines in 4 files covered. (10.53%)

62 existing lines in 2 files now uncovered.

5358 of 9432 relevant lines covered (56.81%)

4.37 hits per line

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

12.1
/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 DataTable from "components/DataTable"
1✔
13
import { ResponseStatus } from "constants/responseStatus"
1✔
14
import { updateStatus } from "features/statusMessageSlice"
1✔
15
import { useAppSelector, useAppDispatch } from "hooks"
1✔
16
import filesAPIService from "services/filesAPI"
1✔
17
import type { DataBucketRow } from "types"
18
import { getMockBucketFiles } from "utils"
1✔
19

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

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

35
  const { t } = useTranslation()
×
36

NEW
37
  const [files, setFiles] = useState<
×
38
    { id: string; path: string; name: string; bytes: number }[] | []
NEW
39
  >([])
×
40

41
  const columns: GridColDef[] = [
×
42
    {
×
43
      field: "name",
×
44
      headerName: t("dataTable.name"),
×
45
      sortable: true,
×
46
      renderCell: params => {
×
47
        return (
×
48
          <Box display="flex" alignItems="center" height="100%">
×
49
            {!bucket && (
×
50
              <Radio
×
51
                checked={selectedBucket === params.row.name}
×
52
                onChange={handleBucketChange}
×
53
                value={params.row.name}
×
54
                name="radio-buttons"
×
55
                inputProps={{ "aria-label": params.row.name }}
×
56
              />
57
            )}
58
            {/* Use mdiPail path: MUI icons lack a corresponding icon */}
59
            <SvgIcon
×
60
              color="primary"
×
61
              fontSize="medium"
×
62
              sx={{ ml: "1rem", mr: "0.5rem" }}
×
63
              onClick={() => handleFilesView(params.row.name)}
×
64
            >
65
              <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" />
×
66
            </SvgIcon>
×
67
            <Typography component="span" onClick={() => handleFilesView(params.row.name)}>
×
68
              {upperFirst(params.row.name)}
×
69
            </Typography>
×
70
          </Box>
×
71
        )
72
      },
×
73
    },
×
74
    {
×
75
      field: "items",
×
76
      headerName: t("dataTable.totalItems"),
×
77
      type: "number",
×
78
    },
×
79
    {
×
80
      field: "size",
×
81
      headerName: t("dataTable.size"),
×
82
      sortable: true, // TODO: need to convert sizes to humanreadable bytes
×
83
    },
×
84
  ]
85

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

NEW
112
  useEffect(() => {
×
NEW
113
    getMockBucketFiles().then(mockFiles => setFiles(mockFiles))
×
NEW
114
  }, [])
×
115

116
  useEffect(() => {
×
117
    !bucket ? setTotalItems(getBucketNames().length) : setTotalItems(1)
×
118
  }, [bucket])
×
119

120
  const getRows = (): DataBucketRow[] => {
×
121
    const bucketNames = getBucketNames()
×
122
    return bucketNames
×
123
      .filter(bucketName => (!!bucket ? bucketName === bucket : bucketName))
×
124
      .map(bucketName => {
×
125
        const currentFiles = files.filter(file => file.path.includes(`/${bucketName}/`))
×
126
        const totalSize = currentFiles.reduce((acc, currentFile) => acc + currentFile["bytes"], 0)
×
127
        return {
×
128
          id: bucketName,
×
129
          name: bucketName,
×
130
          size: totalSize,
×
131
          items: currentFiles.length,
×
132
        }
×
133
      })
×
134
  }
×
135

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

138
  const sortingModel = [
×
139
    {
×
140
      field: "name",
×
141
      sort: "asc" as GridSortDirection,
×
142
    },
×
143
  ]
144

145
  const [page, setPage] = useState<number>(0)
×
146
  const [totalItems, setTotalItems] = useState<number>(0)
×
147

148
  const fetchPageOnChange = (page: number) => {
×
149
    setPage(page)
×
150
  }
×
151

152
  return (
×
153
    <DataTable
×
154
      rows={getRows()}
×
155
      columns={columns}
×
156
      page={page}
×
157
      sortingModel={sortingModel}
×
158
      totalItems={totalItems}
×
159
      fetchPageOnChange={fetchPageOnChange}
×
160
    />
161
  )
162
}
×
163

164
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