github.com/minio/console@v1.4.1/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.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  
    17  import React, { Fragment, useEffect, useState } from "react";
    18  import get from "lodash/get";
    19  import { useSelector } from "react-redux";
    20  import { useNavigate } from "react-router-dom";
    21  import {
    22    ActionLink,
    23    AddIcon,
    24    Box,
    25    Button,
    26    DataTable,
    27    Grid,
    28    HelpBox,
    29    PageLayout,
    30    ProgressBar,
    31    RefreshIcon,
    32    TierOfflineIcon,
    33    TierOnlineIcon,
    34    TiersIcon,
    35    TiersNotAvailableIcon,
    36  } from "mds";
    37  import { api } from "api";
    38  import { errorToHandler } from "api/errors";
    39  import { Tier } from "api/consoleApi";
    40  import { actionsTray } from "../../Common/FormComponents/common/styleLibrary";
    41  import {
    42    CONSOLE_UI_RESOURCE,
    43    IAM_PAGES,
    44    IAM_SCOPES,
    45  } from "../../../../common/SecureComponent/permissions";
    46  import {
    47    hasPermission,
    48    SecureComponent,
    49  } from "../../../../common/SecureComponent";
    50  import { tierTypes } from "./utils";
    51  
    52  import {
    53    selDistSet,
    54    setErrorSnackMessage,
    55    setHelpName,
    56  } from "../../../../systemSlice";
    57  import { useAppDispatch } from "../../../../store";
    58  import SearchBox from "../../Common/SearchBox";
    59  import withSuspense from "../../Common/Components/withSuspense";
    60  import DistributedOnly from "../../Common/DistributedOnly/DistributedOnly";
    61  import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
    62  import PageHeaderWrapper from "../../Common/PageHeaderWrapper/PageHeaderWrapper";
    63  import HelpMenu from "../../HelpMenu";
    64  
    65  const UpdateTierCredentialsModal = withSuspense(
    66    React.lazy(() => import("./UpdateTierCredentialsModal")),
    67  );
    68  
    69  const ListTiersConfiguration = () => {
    70    const dispatch = useAppDispatch();
    71    const navigate = useNavigate();
    72  
    73    const distributedSetup = useSelector(selDistSet);
    74    const [records, setRecords] = useState<Tier[]>([]);
    75    const [filter, setFilter] = useState<string>("");
    76    const [isLoading, setIsLoading] = useState<boolean>(true);
    77    const [updateCredentialsOpen, setUpdateCredentialsOpen] =
    78      useState<boolean>(false);
    79    const [selectedTier, setSelectedTier] = useState<Tier>({
    80      type: "unsupported",
    81      status: false,
    82    });
    83    const hasSetTier = hasPermission(CONSOLE_UI_RESOURCE, [
    84      IAM_SCOPES.ADMIN_SET_TIER,
    85    ]);
    86  
    87    useEffect(() => {
    88      if (isLoading) {
    89        if (distributedSetup) {
    90          const fetchRecords = () => {
    91            api.admin
    92              .tiersList()
    93              .then((res) => {
    94                setRecords(res.data.items || []);
    95                setIsLoading(false);
    96              })
    97              .catch((err) => {
    98                dispatch(setErrorSnackMessage(errorToHandler(err.error)));
    99                setIsLoading(false);
   100              });
   101          };
   102          fetchRecords();
   103        } else {
   104          setIsLoading(false);
   105        }
   106      }
   107    }, [isLoading, dispatch, distributedSetup]);
   108  
   109    const filteredRecords = records.filter((b: Tier) => {
   110      if (filter === "") {
   111        return true;
   112      }
   113      const getItemName = get(b, `${b.type}.name`, "");
   114      const getItemType = get(b, `type`, "");
   115  
   116      return getItemName.indexOf(filter) >= 0 || getItemType.indexOf(filter) >= 0;
   117    });
   118  
   119    const addTier = () => {
   120      navigate(IAM_PAGES.TIERS_ADD);
   121    };
   122  
   123    const renderTierName = (item: Tier) => {
   124      const name = get(item, `${item.type}.name`, "");
   125  
   126      if (name !== null) {
   127        return <b>{name}</b>;
   128      }
   129  
   130      return "";
   131    };
   132  
   133    const renderTierType = (item: string) => {
   134      const { logoXs } =
   135        tierTypes.find((tierConf) => tierConf.serviceName === item) || {};
   136      if (item) {
   137        return (
   138          <Box
   139            sx={{
   140              display: "flex",
   141              alignItems: "center",
   142              "& .min-icon": {
   143                width: "18px",
   144                height: "22px",
   145              },
   146            }}
   147          >
   148            {logoXs}
   149          </Box>
   150        );
   151      }
   152      return "";
   153    };
   154  
   155    const renderTierStatus = (item: boolean) => {
   156      if (item) {
   157        return (
   158          <Grid
   159            container
   160            sx={{
   161              display: "flex",
   162              alignItems: "center",
   163              justifyItems: "start",
   164              color: "#4CCB92",
   165              fontSize: "8px",
   166              flexDirection: "column",
   167            }}
   168          >
   169            <TierOnlineIcon style={{ fill: "#4CCB92", width: 14, height: 14 }} />
   170            ONLINE
   171          </Grid>
   172        );
   173      }
   174      return (
   175        <Grid
   176          container
   177          sx={{
   178            display: "flex",
   179            flexDirection: "column",
   180            alignItems: "center",
   181            color: "#C83B51",
   182            fontSize: "8px",
   183          }}
   184        >
   185          <TierOfflineIcon style={{ fill: "#C83B51", width: 14, height: 14 }} />
   186          OFFLINE
   187        </Grid>
   188      );
   189    };
   190  
   191    const renderTierPrefix = (item: Tier) => {
   192      const prefix = get(item, `${item.type}.prefix`, "");
   193  
   194      if (prefix !== null) {
   195        return prefix;
   196      }
   197  
   198      return "";
   199    };
   200  
   201    const renderTierEndpoint = (item: Tier) => {
   202      const endpoint = get(item, `${item.type}.endpoint`, "");
   203  
   204      if (endpoint !== null) {
   205        return endpoint;
   206      }
   207  
   208      return "";
   209    };
   210  
   211    const renderTierBucket = (item: Tier) => {
   212      const bucket = get(item, `${item.type}.bucket`, "");
   213  
   214      if (bucket !== null) {
   215        return bucket;
   216      }
   217  
   218      return "";
   219    };
   220  
   221    const renderTierRegion = (item: Tier) => {
   222      const region = get(item, `${item.type}.region`, "");
   223  
   224      if (region !== null) {
   225        return region;
   226      }
   227  
   228      return "";
   229    };
   230  
   231    const renderTierUsage = (item: Tier) => {
   232      const endpoint = get(item, `${item.type}.usage`, "");
   233  
   234      if (endpoint !== null) {
   235        return endpoint;
   236      }
   237  
   238      return "";
   239    };
   240  
   241    const renderTierObjects = (item: Tier) => {
   242      const endpoint = get(item, `${item.type}.objects`, "");
   243  
   244      if (endpoint !== null) {
   245        return endpoint;
   246      }
   247  
   248      return "";
   249    };
   250  
   251    const renderTierVersions = (item: Tier) => {
   252      const endpoint = get(item, `${item.type}.versions`, "");
   253  
   254      if (endpoint !== null) {
   255        return endpoint;
   256      }
   257  
   258      return "";
   259    };
   260  
   261    const closeTierCredentials = () => {
   262      setUpdateCredentialsOpen(false);
   263    };
   264  
   265    useEffect(() => {
   266      dispatch(setHelpName("list-tiers-configuration"));
   267      // eslint-disable-next-line react-hooks/exhaustive-deps
   268    }, []);
   269  
   270    return (
   271      <Fragment>
   272        {updateCredentialsOpen && (
   273          <UpdateTierCredentialsModal
   274            open={updateCredentialsOpen}
   275            tierData={selectedTier}
   276            closeModalAndRefresh={closeTierCredentials}
   277          />
   278        )}
   279        <PageHeaderWrapper label="Tiers" actions={<HelpMenu />} />
   280  
   281        <PageLayout>
   282          {!distributedSetup ? (
   283            <DistributedOnly
   284              entity={"Tiers"}
   285              iconComponent={<TiersNotAvailableIcon />}
   286            />
   287          ) : (
   288            <Fragment>
   289              <Grid item xs={12} sx={actionsTray.actionsTray}>
   290                <SearchBox
   291                  placeholder="Filter"
   292                  onChange={setFilter}
   293                  value={filter}
   294                  sx={{
   295                    marginRight: "auto",
   296                    maxWidth: 380,
   297                  }}
   298                />
   299  
   300                <Box
   301                  sx={{
   302                    display: "flex",
   303                    flexWrap: "nowrap",
   304                    gap: 5,
   305                  }}
   306                >
   307                  <Button
   308                    id={"refresh-list"}
   309                    icon={<RefreshIcon />}
   310                    label={`Refresh List`}
   311                    onClick={() => {
   312                      setIsLoading(true);
   313                    }}
   314                  />
   315                  <TooltipWrapper
   316                    tooltip={
   317                      hasSetTier
   318                        ? ""
   319                        : "You require additional permissions in order to create a new Tier. Please ask your MinIO administrator to grant you " +
   320                          IAM_SCOPES.ADMIN_SET_TIER +
   321                          " permission in order to create a Tier."
   322                    }
   323                  >
   324                    <SecureComponent
   325                      scopes={[IAM_SCOPES.ADMIN_SET_TIER]}
   326                      resource={CONSOLE_UI_RESOURCE}
   327                      errorProps={{ disabled: true }}
   328                    >
   329                      <Button
   330                        id={"add-tier"}
   331                        icon={<AddIcon />}
   332                        label={`Create Tier`}
   333                        onClick={addTier}
   334                        variant="callAction"
   335                      />
   336                    </SecureComponent>
   337                  </TooltipWrapper>
   338                </Box>
   339              </Grid>
   340              {isLoading && <ProgressBar />}
   341              {!isLoading && (
   342                <Fragment>
   343                  {records.length > 0 && (
   344                    <Fragment>
   345                      <Grid item xs={12}>
   346                        <SecureComponent
   347                          scopes={[IAM_SCOPES.ADMIN_LIST_TIERS]}
   348                          resource={CONSOLE_UI_RESOURCE}
   349                          errorProps={{ disabled: true }}
   350                        >
   351                          <DataTable
   352                            itemActions={[
   353                              {
   354                                type: "edit",
   355                                onClick: (tierData: Tier) => {
   356                                  setSelectedTier(tierData);
   357                                  setUpdateCredentialsOpen(true);
   358                                },
   359                              },
   360                            ]}
   361                            columns={[
   362                              {
   363                                label: "Tier Name",
   364                                elementKey: "type",
   365                                renderFunction: renderTierName,
   366                                renderFullObject: true,
   367                              },
   368                              {
   369                                label: "Status",
   370                                elementKey: "status",
   371                                renderFunction: renderTierStatus,
   372                                width: 50,
   373                              },
   374                              {
   375                                label: "Type",
   376                                elementKey: "type",
   377                                renderFunction: renderTierType,
   378                                width: 50,
   379                              },
   380                              {
   381                                label: "Endpoint",
   382                                elementKey: "type",
   383                                renderFunction: renderTierEndpoint,
   384                                renderFullObject: true,
   385                              },
   386                              {
   387                                label: "Bucket",
   388                                elementKey: "type",
   389                                renderFunction: renderTierBucket,
   390                                renderFullObject: true,
   391                              },
   392                              {
   393                                label: "Prefix",
   394                                elementKey: "type",
   395                                renderFunction: renderTierPrefix,
   396                                renderFullObject: true,
   397                              },
   398                              {
   399                                label: "Region",
   400                                elementKey: "type",
   401                                renderFunction: renderTierRegion,
   402                                renderFullObject: true,
   403                              },
   404                              {
   405                                label: "Usage",
   406                                elementKey: "type",
   407                                renderFunction: renderTierUsage,
   408                                renderFullObject: true,
   409                              },
   410                              {
   411                                label: "Objects",
   412                                elementKey: "type",
   413                                renderFunction: renderTierObjects,
   414                                renderFullObject: true,
   415                              },
   416                              {
   417                                label: "Versions",
   418                                elementKey: "type",
   419                                renderFunction: renderTierVersions,
   420                                renderFullObject: true,
   421                              },
   422                            ]}
   423                            isLoading={isLoading}
   424                            records={filteredRecords}
   425                            entityName="Tiers"
   426                            idField="service_name"
   427                            customPaperHeight={"400px"}
   428                          />
   429                        </SecureComponent>
   430                      </Grid>
   431                      <Grid
   432                        item
   433                        xs={12}
   434                        sx={{
   435                          marginTop: "15px",
   436                        }}
   437                      >
   438                        <HelpBox
   439                          title={"Learn more about TIERS"}
   440                          iconComponent={<TiersIcon />}
   441                          help={
   442                            <Fragment>
   443                              Tiers are used by the MinIO Object Lifecycle
   444                              Management which allows creating rules for time or
   445                              date based automatic transition or expiry of
   446                              objects. For object transition, MinIO automatically
   447                              moves the object to a configured remote storage
   448                              tier.
   449                              <br />
   450                              <br />
   451                              You can learn more at our{" "}
   452                              <a
   453                                href="https://min.io/docs/minio/linux/administration/object-management/object-lifecycle-management.html?ref=con"
   454                                target="_blank"
   455                                rel="noopener"
   456                              >
   457                                documentation
   458                              </a>
   459                              .
   460                            </Fragment>
   461                          }
   462                        />
   463                      </Grid>
   464                    </Fragment>
   465                  )}
   466                  {records.length === 0 && (
   467                    <HelpBox
   468                      title={"Tiers"}
   469                      iconComponent={<TiersIcon />}
   470                      help={
   471                        <Fragment>
   472                          Tiers are used by the MinIO Object Lifecycle Management
   473                          which allows creating rules for time or date based
   474                          automatic transition or expiry of objects. For object
   475                          transition, MinIO automatically moves the object to a
   476                          configured remote storage tier.
   477                          <br />
   478                          <br />
   479                          {hasSetTier ? (
   480                            <div>
   481                              To get started,{" "}
   482                              <ActionLink
   483                                isLoading={false}
   484                                label={""}
   485                                onClick={addTier}
   486                              >
   487                                Create Tier
   488                              </ActionLink>
   489                              .
   490                            </div>
   491                          ) : (
   492                            ""
   493                          )}
   494                        </Fragment>
   495                      }
   496                    />
   497                  )}
   498                </Fragment>
   499              )}
   500            </Fragment>
   501          )}
   502        </PageLayout>
   503      </Fragment>
   504    );
   505  };
   506  
   507  export default ListTiersConfiguration;