Skip to content

Commit

Permalink
Fix bugs in UI pagination
Browse files Browse the repository at this point in the history
Motivation:

- When a filter value is set, the rows are filtered by the value but the
  original page index is not updated correctly.
- `New repository` button wasn't shown up in the anonymous mode.

Modifications:

- Fixed `Filter` to set the page index to 0 when a filter value is
  updated.
- Fixed `WithProjectRole` component to show the child components in
  the anonymous mode.

Result:

- Pagination works properly even when filters are applied.
- The new repository button is now correctly exposed in anonymous mode.
  • Loading branch information
ikhoon committed Aug 20, 2024
1 parent b3a3515 commit 4b5c815
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 21 deletions.
4 changes: 4 additions & 0 deletions webapp/src/dogma/common/components/UserRole.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Badge } from '@chakra-ui/react';

function badgeColor(role: string) {
if (!role) {
return 'gray';
}

switch (role.toLowerCase()) {
case 'user':
case 'member':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ export const DataTableClientPagination = <Data extends object>({
return (
<>
<Text mb="8px">Filter by {table.getHeaderGroups()[0].headers[0].id} </Text>
<Filter column={table.getHeaderGroups()[0].headers[0].column /* Filter by the 1st column */} />
<Filter
table={table}
column={table.getHeaderGroups()[0].headers[0].column /* Filter by the 1st column */}
/>
<DataTable table={table} aria-label={''} />
<PaginationBar table={table} />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ export const DynamicDataTable = <Data extends object>({
return (
<>
<Text mb="8px">Filter by {table.getHeaderGroups()[0].headers[0].id} </Text>
<Filter column={table.getHeaderGroups()[0].headers[0].column /* Filter by the 1st column */} />
<Filter
table={table}
column={table.getHeaderGroups()[0].headers[0].column /* Filter by the 1st column */}
/>
<DataTable table={table} />
{pagination && <PaginationBar table={table} />}
</>
Expand Down
13 changes: 10 additions & 3 deletions webapp/src/dogma/common/components/table/Filter.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { Column } from '@tanstack/react-table';
import { Column, Table } from '@tanstack/react-table';
import { DebouncedInput } from 'dogma/common/components/table/DebouncedInput';
import { useCallback, useMemo } from 'react';

export type FilterProps<Data> = {
table: Table<Data>;
column: Column<Data, unknown>;
};

export const Filter = <Data extends object>({ column }: FilterProps<Data>) => {
export const Filter = <Data extends object>({ table, column }: FilterProps<Data>) => {
const columnFilterValue = column.getFilterValue();
const facetedUniqueValues = column.getFacetedUniqueValues();
const sortedUniqueValues = useMemo(
() => Array.from(facetedUniqueValues.keys()).sort(),
[facetedUniqueValues],
);
const handleChange = useCallback((value: string | number) => column.setFilterValue(value), [column]);
const handleChange = useCallback(
(value: string | number) => {
table.setPageIndex(0);
column.setFilterValue(value);
},
[table, column],
);

return (
<>
Expand Down
8 changes: 4 additions & 4 deletions webapp/src/dogma/common/components/table/PaginationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ export const PaginationBar = <Data extends object>({ table }: { table: ReactTabl
aria-label="First page"
icon={<MdSkipPrevious />}
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
isDisabled={!table.getCanPreviousPage()}
/>
<IconButton
aria-label="Prev page"
icon={<MdNavigateBefore />}
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
isDisabled={!table.getCanPreviousPage()}
/>
<IconButton
aria-label="Next page"
icon={<MdNavigateNext />}
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
isDisabled={!table.getCanNextPage()}
/>
<IconButton
aria-label="Last page"
icon={<MdSkipNext />}
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
isDisabled={!table.getCanNextPage()}
/>
<Text>Page</Text>
<Text fontWeight="bold">
Expand Down
6 changes: 5 additions & 1 deletion webapp/src/dogma/features/auth/ProjectRole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ export const WithProjectRole = ({ projectName, roles, children }: WithProjectRol
refetchOnFocus: true,
});

const { user } = useAppSelector((state) => state.auth);
const { user, isInAnonymousMode } = useAppSelector((state) => state.auth);
if (isInAnonymousMode) {
return <>{children()}</>;
}

const role = findUserRole(user, metadata);

if (roles.find((r) => r === role)) {
Expand Down
9 changes: 9 additions & 0 deletions webapp/src/dogma/features/auth/authSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ const initialState: AuthState = {
isLoading: true,
};

const anonymousUser: UserDto = {
login: 'anonymous',
name: 'Anonymous',
email: 'anonymous@localhost',
roles: [],
admin: false,
};

export const authSlice = createSlice({
name: 'auth',
initialState,
Expand All @@ -138,6 +146,7 @@ export const authSlice = createSlice({
state.isInAnonymousMode = true;
state.sessionId = '';
state.isLoading = false;
state.user = anonymousUser;
})
.addCase(login.fulfilled, (state, { payload }) => {
state.sessionId = payload.access_token;
Expand Down
31 changes: 20 additions & 11 deletions webapp/src/dogma/features/project/settings/ProjectSettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,33 @@ interface ProjectSettingsViewProps {
}

type TabName = 'repositories' | 'permissions' | 'members' | 'tokens' | 'mirrors' | 'credentials';
type UserRole = 'OWNER' | 'MEMBER' | 'GUEST' | 'ANONYMOUS';

export interface TapInfo {
name: TabName;
path: string;
accessRole: 'OWNER' | 'MEMBER' | 'GUEST';
accessRole: UserRole;
allowAnonymous: boolean;
}

const TABS: TapInfo[] = [
// 'repositories' is the index tab
{ name: 'repositories', path: '', accessRole: 'GUEST' },
{ name: 'permissions', path: 'permissions', accessRole: 'OWNER' },
{ name: 'members', path: 'members', accessRole: 'OWNER' },
{ name: 'tokens', path: 'tokens', accessRole: 'OWNER' },
{ name: 'mirrors', path: 'mirrors', accessRole: 'OWNER' },
{ name: 'credentials', path: 'credentials', accessRole: 'OWNER' },
{ name: 'repositories', path: '', accessRole: 'GUEST', allowAnonymous: true },
{ name: 'permissions', path: 'permissions', accessRole: 'OWNER', allowAnonymous: false },
{ name: 'members', path: 'members', accessRole: 'OWNER', allowAnonymous: false },
{ name: 'tokens', path: 'tokens', accessRole: 'OWNER', allowAnonymous: false },
{ name: 'mirrors', path: 'mirrors', accessRole: 'OWNER', allowAnonymous: true },
{ name: 'credentials', path: 'credentials', accessRole: 'OWNER', allowAnonymous: true },
];

function isAllowed(userRole: string, tabInfo: TapInfo): boolean {
function isAllowed(userRole: string, anonymous: boolean, tabInfo: TapInfo): boolean {
if (!tabInfo) {
return false;
}
if (anonymous && tabInfo.allowAnonymous) {
return true;
}

switch (tabInfo.accessRole) {
case 'OWNER':
return userRole === 'OWNER';
Expand All @@ -64,7 +73,7 @@ function isAllowed(userRole: string, tabInfo: TapInfo): boolean {
}

const ProjectSettingsView = ({ projectName, currentTab, children }: ProjectSettingsViewProps) => {
const { user } = useAppSelector((state) => state.auth);
const { user, isInAnonymousMode } = useAppSelector((state) => state.auth);
const tabIndex = TABS.findIndex((tab) => tab.name === currentTab);
const router = useRouter();

Expand Down Expand Up @@ -104,7 +113,7 @@ const ProjectSettingsView = ({ projectName, currentTab, children }: ProjectSetti
<Tabs variant="enclosed-colored" size="lg" index={tabIndex}>
<TabList>
{TABS.map((tab) => {
const allowed = isAllowed(accessRole, tab);
const allowed = isAllowed(accessRole, isInAnonymousMode, tab);
let link = '';
if (allowed) {
link = `/app/projects/${projectName}/settings`;
Expand All @@ -124,7 +133,7 @@ const ProjectSettingsView = ({ projectName, currentTab, children }: ProjectSetti
</TabList>
<TabPanels>
{TABS.map((tab) => {
const allowed = isAllowed(accessRole, tab);
const allowed = isAllowed(accessRole, isInAnonymousMode, tab);
return (
<TabPanel key={tab.name}>{tab.name === currentTab && allowed && children(metadata)}</TabPanel>
);
Expand Down

0 comments on commit 4b5c815

Please sign in to comment.