feat(usage): treat org owners as managers on the Usage page
The Usage page gated cross-user features (default ``scope=all``, the user filter, the per-user breakdown dimension) on ``currentUser.is_admin`` alone. The backend's ``_can_use_all_scope`` already grants the same surface to Org owners — the page just never asked. Result: a non-admin Org owner landed on ``scope=self`` with no user filter and no per-user group-by, even though the API would have happily served them the org-wide picture. Switch the three sites to ``useAccess().canSeeOrgAdmin``, which already encodes "platform admin OR owner of the selected (non-Personal) Org". One predicate, no new helper. Sites: - ``index.tsx``: ``initialScope`` defaults to ``all`` for managers. - ``filter-bar.tsx``: the user filter + nested API-key cascader surface (vs the flat API-key picker for members). - ``daily-usage.tsx``: the ``user`` group-by option in the chart.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import useCoolColors from '@/hooks/use-cool-colors';
|
||||
import BarChart from '@/pages/_components/bar-chart';
|
||||
import { BaseSelect, CardWrapper } from '@gpustack/core-ui';
|
||||
import { useIntl, useModel } from '@umijs/max';
|
||||
import { useAccess, useIntl } from '@umijs/max';
|
||||
import { Segmented } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import React, { useMemo } from 'react';
|
||||
@@ -80,8 +80,12 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
|
||||
onGranularityChange
|
||||
} = props;
|
||||
const generateCoolColors = useCoolColors();
|
||||
const { initialState } = useModel('@@initialState');
|
||||
const { currentUser } = initialState || {};
|
||||
// ``canSeeOrgAdmin`` widens to Org owners of the selected Org
|
||||
// (Personal Org excluded). Mirrors the BE's
|
||||
// ``_can_use_all_scope`` gate for the per-user breakdown
|
||||
// dimension.
|
||||
const access = useAccess();
|
||||
const canGroupByUser = !!access.canSeeOrgAdmin;
|
||||
|
||||
const labelFormatter = (v: any) => {
|
||||
if (granularity === 'month') {
|
||||
@@ -253,7 +257,7 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
|
||||
value: item.value
|
||||
}))
|
||||
.filter((option) => {
|
||||
if (currentUser?.is_admin) {
|
||||
if (canGroupByUser) {
|
||||
return true;
|
||||
}
|
||||
return option.value !== 'user';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import useRangePickerPreset from '@/pages/dashboard/hooks/use-rangepicker-preset';
|
||||
import { useModel } from '@@/plugin-model';
|
||||
import { DownloadOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
AutoTooltip,
|
||||
@@ -7,7 +6,7 @@ import {
|
||||
IconFont,
|
||||
SimpleSelect
|
||||
} from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useAccess, useIntl } from '@umijs/max';
|
||||
import { Button, DatePicker, Dropdown, MenuProps } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import React from 'react';
|
||||
@@ -119,8 +118,12 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
||||
]
|
||||
});
|
||||
|
||||
const initialInfo = useModel('@@initialState');
|
||||
const { initialState } = initialInfo || {};
|
||||
// ``canSeeOrgAdmin`` already encodes "platform admin OR owner of the
|
||||
// selected (non-Personal) Org" via the access seam. Mirrors the
|
||||
// backend's ``_can_use_all_scope`` predicate for the user-filter
|
||||
// drill-down surface.
|
||||
const access = useAccess();
|
||||
const canManageUsers = !!access.canSeeOrgAdmin;
|
||||
|
||||
const exportMenuItems: MenuProps['items'] = [
|
||||
{
|
||||
@@ -252,7 +255,7 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
||||
optionLabelRender={singleOptionRender}
|
||||
onChange={onRoutesChange}
|
||||
/>
|
||||
{initialState?.currentUser?.is_admin && (
|
||||
{canManageUsers && (
|
||||
<>
|
||||
<SimpleSelect
|
||||
allowClear
|
||||
@@ -309,7 +312,7 @@ const FilterBar: React.FC<FilterBarProps> = (props) => {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{!initialState?.currentUser?.is_admin && (
|
||||
{!canManageUsers && (
|
||||
<SimpleSelect
|
||||
allowClear
|
||||
showSearch
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { baseColorMap } from '@/pages/dashboard/config';
|
||||
import { formatLargeNumber } from '@/utils';
|
||||
import { useModel } from '@@/plugin-model';
|
||||
import { SimpleCard } from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { useAccess, useIntl } from '@umijs/max';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import BreakdownTabs from './components/breakdown-tabs';
|
||||
import DailyUsage from './components/daily-usage';
|
||||
@@ -16,8 +15,7 @@ type DateType = 'date' | 'week' | 'month' | 'quarter' | 'year';
|
||||
|
||||
const Usage: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const initialInfo = useModel('@@initialState');
|
||||
const { initialState } = initialInfo || {};
|
||||
const access = useAccess();
|
||||
const { exportTable } = useExportTable();
|
||||
const [openExportModal, setOpenExportModal] = useState(false);
|
||||
const [breakdownRefreshKey, setBreakdownRefreshKey] = useState(0);
|
||||
@@ -66,7 +64,10 @@ const Usage: React.FC = () => {
|
||||
|
||||
const { filters, commonFilters, fetchData, timeSeriesData, filterBar } =
|
||||
useUsageFilters({
|
||||
initialScope: initialState?.currentUser?.is_admin ? 'all' : 'self',
|
||||
// ``canSeeOrgAdmin`` widens to Org owners of the selected Org in
|
||||
// the enterprise build (Personal Org excluded). Mirrors the BE's
|
||||
// ``_can_use_all_scope`` gate one-to-one.
|
||||
initialScope: access.canSeeOrgAdmin ? 'all' : 'self',
|
||||
metaData,
|
||||
chartFilters,
|
||||
summaryColumns
|
||||
|
||||
Reference in New Issue
Block a user