github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/ListBuckets/BucketListItem.tsx (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  import React, { Fragment, useState } from "react";
    17  import get from "lodash/get";
    18  import styled from "styled-components";
    19  import { Link, useNavigate } from "react-router-dom";
    20  import {
    21    Box,
    22    breakPoints,
    23    BucketsIcon,
    24    Checkbox,
    25    Grid,
    26    HelpTip,
    27    ReportedUsageIcon,
    28    TotalObjectsIcon,
    29  } from "mds";
    30  import {
    31    calculateBytes,
    32    niceBytes,
    33    prettyNumber,
    34  } from "../../../../common/utils";
    35  import {
    36    IAM_PERMISSIONS,
    37    IAM_ROLES,
    38  } from "../../../../common/SecureComponent/permissions";
    39  import { hasPermission } from "../../../../common/SecureComponent";
    40  import { Bucket } from "../../../../api/consoleApi";
    41  import { usageClarifyingContent } from "screens/Console/Dashboard/BasicDashboard/ReportedUsage";
    42  
    43  const BucketItemMain = styled.div(({ theme }) => ({
    44    border: `${get(theme, "borderColor", "#eaeaea")} 1px solid`,
    45    borderRadius: 3,
    46    padding: 15,
    47    cursor: "pointer",
    48    "&.disabled": {
    49      backgroundColor: get(theme, "signalColors.danger", "red"),
    50    },
    51    "&:hover": {
    52      backgroundColor: get(theme, "boxBackground", "#FBFAFA"),
    53    },
    54    "& .bucketTitle": {
    55      display: "flex",
    56      alignItems: "center",
    57      justifyContent: "flex-start",
    58      gap: 10,
    59      "& h1": {
    60        padding: 0,
    61        margin: 0,
    62        marginBottom: 5,
    63        fontSize: 22,
    64        color: get(theme, "screenTitle.iconColor", "#07193E"),
    65        [`@media (max-width: ${breakPoints.md}px)`]: {
    66          marginBottom: 0,
    67        },
    68      },
    69    },
    70    "& .bucketDetails": {
    71      display: "flex",
    72      gap: 40,
    73      "& span": {
    74        fontSize: 14,
    75      },
    76      [`@media (max-width: ${breakPoints.md}px)`]: {
    77        flexFlow: "column-reverse",
    78        gap: 5,
    79      },
    80    },
    81    "& .bucketMetrics": {
    82      display: "flex",
    83      alignItems: "center",
    84      marginTop: 20,
    85      gap: 25,
    86      borderTop: `${get(theme, "borderColor", "#E2E2E2")} 1px solid`,
    87      paddingTop: 20,
    88      "& svg.bucketIcon": {
    89        color: get(theme, "screenTitle.iconColor", "#07193E"),
    90        fill: get(theme, "screenTitle.iconColor", "#07193E"),
    91      },
    92      "& .metric": {
    93        "& .min-icon": {
    94          color: get(theme, "fontColor", "#000"),
    95          width: 13,
    96          marginRight: 5,
    97        },
    98      },
    99      "& .metricLabel": {
   100        fontSize: 14,
   101        fontWeight: "bold",
   102        color: get(theme, "fontColor", "#000"),
   103      },
   104      "& .metricText": {
   105        fontSize: 24,
   106        fontWeight: "bold",
   107      },
   108      "& .unit": {
   109        fontSize: 12,
   110        fontWeight: "normal",
   111      },
   112      [`@media (max-width: ${breakPoints.md}px)`]: {
   113        marginTop: 8,
   114        paddingTop: 8,
   115      },
   116    },
   117  }));
   118  
   119  interface IBucketListItem {
   120    bucket: Bucket;
   121    onSelect: (e: React.ChangeEvent<HTMLInputElement>) => void;
   122    selected: boolean;
   123    bulkSelect: boolean;
   124  }
   125  
   126  const BucketListItem = ({
   127    bucket,
   128    onSelect,
   129    selected,
   130    bulkSelect,
   131  }: IBucketListItem) => {
   132    const navigate = useNavigate();
   133  
   134    const [clickOverride, setClickOverride] = useState<boolean>(false);
   135  
   136    const usage = niceBytes(`${bucket.size}` || "0");
   137    const usageScalar = usage.split(" ")[0];
   138    const usageUnit = usage.split(" ")[1];
   139  
   140    const quota = get(bucket, "details.quota.quota", "0");
   141    const quotaForString = calculateBytes(quota, true, false);
   142  
   143    const manageAllowed =
   144      hasPermission(bucket.name, IAM_PERMISSIONS[IAM_ROLES.BUCKET_ADMIN]) &&
   145      false;
   146  
   147    const accessToStr = (bucket: Bucket): string => {
   148      if (bucket.rw_access?.read && !bucket.rw_access?.write) {
   149        return "R";
   150      } else if (!bucket.rw_access?.read && bucket.rw_access?.write) {
   151        return "W";
   152      } else if (bucket.rw_access?.read && bucket.rw_access?.write) {
   153        return "R/W";
   154      }
   155      return "";
   156    };
   157    const onCheckboxClick = (e: React.ChangeEvent<HTMLInputElement>) => {
   158      onSelect(e);
   159    };
   160  
   161    return (
   162      <BucketItemMain
   163        onClick={() => {
   164          !clickOverride && navigate(`/buckets/${bucket.name}/admin`);
   165        }}
   166        id={`manageBucket-${bucket.name}`}
   167        className={`bucket-item ${manageAllowed ? "disabled" : ""}`}
   168      >
   169        <Box className={"bucketTitle"}>
   170          {bulkSelect && (
   171            <Box
   172              onClick={(e) => {
   173                e.stopPropagation();
   174              }}
   175            >
   176              <Checkbox
   177                checked={selected}
   178                id={`select-${bucket.name}`}
   179                label={""}
   180                name={`select-${bucket.name}`}
   181                onChange={onCheckboxClick}
   182                value={bucket.name}
   183              />
   184            </Box>
   185          )}
   186          <h1>
   187            {bucket.name} {manageAllowed}
   188          </h1>
   189        </Box>
   190        <Box className={"bucketDetails"}>
   191          <span id={`created-${bucket.name}`}>
   192            <strong>Created:</strong>{" "}
   193            {bucket.creation_date
   194              ? new Date(bucket.creation_date).toString()
   195              : "n/a"}
   196          </span>
   197          <span id={`access-${bucket.name}`}>
   198            <strong>Access:</strong> {accessToStr(bucket)}
   199          </span>
   200        </Box>
   201        <Box className={"bucketMetrics"}>
   202          <Link to={`/buckets/${bucket.name}/admin`}>
   203            <BucketsIcon
   204              className={"bucketIcon"}
   205              style={{
   206                height: 48,
   207                width: 48,
   208              }}
   209            />
   210          </Link>
   211  
   212          <Grid
   213            item
   214            className={"metric"}
   215            onMouseEnter={() =>
   216              bucket.details?.versioning && setClickOverride(true)
   217            }
   218            onMouseLeave={() =>
   219              bucket.details?.versioning && setClickOverride(false)
   220            }
   221          >
   222            {bucket.details?.versioning && (
   223              <HelpTip content={usageClarifyingContent} placement="top">
   224                <ReportedUsageIcon />{" "}
   225              </HelpTip>
   226            )}
   227            {!bucket.details?.versioning && <ReportedUsageIcon />}
   228            <span className={"metricLabel"}>Usage</span>
   229            <div className={"metricText"}>
   230              {usageScalar}
   231              <span className={"unit"}>{usageUnit}</span>
   232              {quota !== "0" && (
   233                <Fragment>
   234                  {" "}
   235                  / {quotaForString.total}
   236                  <span className={"unit"}>{quotaForString.unit}</span>
   237                </Fragment>
   238              )}
   239            </div>
   240          </Grid>
   241  
   242          <Grid item className={"metric"}>
   243            <TotalObjectsIcon />
   244            <span className={"metricLabel"}>Objects</span>
   245            <div className={"metricText"}>
   246              {bucket.objects ? prettyNumber(bucket.objects) : 0}
   247            </div>
   248          </Grid>
   249        </Box>
   250      </BucketItemMain>
   251    );
   252  };
   253  
   254  export default BucketListItem;