Skip to content

Commit

Permalink
Convert UserTokens
Browse files Browse the repository at this point in the history
syntax updates to AddTokenModal

simplification of tokens page, remove pagination from URL params
  • Loading branch information
mshriver committed Feb 19, 2025
1 parent daf5983 commit bbbd416
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 199 deletions.
6 changes: 3 additions & 3 deletions frontend/src/components/add-token-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { HttpClient } from '../services/http';
import { Settings } from '../settings';

function AddTokenModal (props) {
const AddTokenModal = (props) => {
const [name, setName] = useState('');
const [expiryDate, setExpiryDate] = useState('');
const [isNameValid, setIsNameValid] = useState(true);
Expand All @@ -28,7 +28,7 @@ function AddTokenModal (props) {
onClose,
} = props;

function onSave () {
const onSave = () => {
const expiry = new Date(expiryDate);
const now = new Date();

Expand Down Expand Up @@ -60,7 +60,7 @@ function AddTokenModal (props) {

};

function localOnClose () {
const localOnClose = () => {
// call prop function
onClose();

Expand Down
280 changes: 102 additions & 178 deletions frontend/src/pages/profile/tokens.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ToastContainer, toast } from 'react-toastify';
import React, { useEffect, useState } from 'react';
import { ToastContainer } from 'react-toastify';

import {
Button,
Expand All @@ -11,8 +10,8 @@ import {
FlexItem,
PageSection,
PageSectionVariants,
Text,
TextContent,
Title,
} from '@patternfly/react-core';
import { PlusCircleIcon } from '@patternfly/react-icons';

Expand All @@ -22,62 +21,23 @@ import { getSpinnerRow } from '../../utilities';
import { FilterTable } from '../../components/filtertable';
import AddTokenModal from '../../components/add-token-modal';
import DeleteModal from '../../components/delete-modal';
import ToastWrapper from '../../components/toast-wrapper';
import { ALERT_TIMEOUT } from '../../constants';

const COLUMNS = ['Name', 'Token', 'Expires', ''];

export class UserTokens extends React.Component {
const UserTokens = () => {

static propTypes = {
location: PropTypes.object,
navigate: PropTypes.func,
eventEmitter: PropTypes.object
};
const [tokens, setTokens] = useState([]);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(20);
const [totalItems, setTotalItems] = useState(0);
const [isError, setIsError] = useState(false);
const [isAddTokenOpen, setIsAddTokenOpen] = useState(false);
const [isDeleteTokenOpen, setIsDeleteTokenOpen] = useState(false);
const [tokenToDelete, setTokenToDelete] = useState();

constructor (props) {
super(props);
this.eventEmitter = props.eventEmitter;
const params = new URLSearchParams(props.location.search);
let page = 1, pageSize = 20;
if (params.toString() !== '') {
for(let pair of params) {
if (pair[0] === 'page') {
page = parseInt(pair[1]);
}
else if (pair[0] === 'pageSize') {
pageSize = parseInt(pair[1]);
}
}
}
this.state = {
columns: ['Name', 'Token', 'Expires', ''],
rows: [getSpinnerRow(5)],
page: page,
pageSize: pageSize,
totalItems: 0,
totalPages: 0,
isError: false,
isEmpty: false,
isAddTokenOpen: false,
isDeleteTokenOpen: false,
tokenToDelete: null,
};
}


copyToClipboard = (text) => {
navigator.clipboard.writeText(text);
toast(<ToastWrapper />,
{
data: {
type: 'info',
title: 'Copied to clipboard',
message: 'Your token has been copied to the clipboard'
}});
};

tokenToRow (token) {
return {
const tokenToRow = (token) => {
const shutupEslint = {
'cells': [
{title: token.name},
{title: (
Expand All @@ -86,134 +46,98 @@ export class UserTokens extends React.Component {
</ClipboardCopy>
)},
{title: token.expires},
{title: <Button variant="danger" onClick={() => this.onDeleteTokenClick(token)}>Delete</Button>}
{title: <Button variant="danger" onClick={() => {
setTokenToDelete(token);
setIsDeleteTokenOpen(true);
}}>Delete</Button>}
]
};
}

updateUrl () {
let params = [];
params.push('page=' + this.state.page);
params.push('pageSize=' + this.state.pageSize);
this.props.navigate('/profile/tokens?' + params.join('&'));
}

setPage = (_event, pageNumber) => {
this.setState({page: pageNumber}, () => {
this.updateUrl();
this.getTokens();
});
return(shutupEslint);
};

setPageSize = (_event, perPage) => {
this.setState({pageSize: perPage}, () => {
this.updateUrl();
this.getTokens();
});
};

// TODO: useEffect on add and delete modal close
getTokens () {
// First, show a spinner
this.setState({rows: [getSpinnerRow(4)], isEmpty: false, isError: false});
let params = {
'pageSize': this.state.pageSize,
'page': this.state.page
};
HttpClient.get([Settings.serverUrl, 'user', 'token'], params)
useEffect(() => {
HttpClient.get([Settings.serverUrl, 'user', 'token'], {page: page, pageSize: pageSize})
.then(response => HttpClient.handleResponse(response))
.then(data => this.setState({
rows: data.tokens.map((token) => this.tokenToRow(token)),
page: data.pagination.page,
pageSize: data.pagination.pageSize,
totalItems: data.pagination.totalItems,
totalPages: data.pagination.totalPages,
isEmpty: data.pagination.totalItems === 0
}))
.then(data => {
if (data?.tokens?.length > 0) {
setTokens(data.tokens);
setPage(data.pagination.page);
setPageSize(data.pagination.pageSize);
setTotalItems(data.pagination.totalItems);
} else {
setTokens([]);
}

})
.catch((error) => {
console.error('Error fetching token data:', error);
this.setState({rows: [], isEmpty: false, isError: true});
setTokens([]);
setIsError(true);
});
}

onAddTokenClick = () => {
this.setState({isAddTokenOpen: true});
};

onAddTokenClose = () => {
this.setState({isAddTokenOpen: false});
};

onDeleteTokenClick = (token) => {
this.setState({tokenToDelete: token, isDeleteTokenOpen: true});
};

onDeleteTokenClose = () => {
this.setState({tokenToDelete: null, isDeleteTokenOpen: false});
};

componentDidMount () {
this.getTokens();
}

render () {
document.title = 'User Tokens | Ibutsu';
const { columns, rows } = this.state;
const pagination = {
pageSize: this.state.pageSize,
page: this.state.page,
totalItems: this.state.totalItems
};
return (
<React.Fragment>
<PageSection id="page" variant={PageSectionVariants.light}>
<Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
<FlexItem spacer={{ default: 'spacerLg' }}>
<TextContent>
<Text className="title" component="h1">Tokens</Text>
</TextContent>
</FlexItem>
<FlexItem>
<Button
aria-label="Add token"
variant="secondary"
title="Add token"
onClick={this.onAddTokenClick}
>
<PlusCircleIcon /> Add Token
</Button>
</FlexItem>
</Flex>
</PageSection>
<PageSection>
<Card>
<CardBody className="pf-u-p-0">
<FilterTable
columns={columns}
rows={rows}
pagination={pagination}
isEmpty={this.state.isEmpty}
isError={this.state.isError}
onSetPage={this.setPage}
onSetPageSize={this.setPageSize}
/>
</CardBody>
</Card>
</PageSection>
<AddTokenModal
isOpen={this.state.isAddTokenOpen}
onClose={this.onAddTokenClose}
/>
<DeleteModal
title="Delete token"
body="Would you like to delete the selected token?"
toDeleteId={this.state.tokenToDelete?.id}
toDeletePath={['user', 'token']}
isOpen={this.state.isDeleteTokenOpen}
onClose={this.onDeleteTokenClose}
/>
<ToastContainer autoClose={ALERT_TIMEOUT} stacked/>
</React.Fragment>
);
}
}
}, [page, pageSize, isAddTokenOpen, isDeleteTokenOpen]); // extra deps to trigger fetch on modal close


document.title = 'User Tokens | Ibutsu';
return (
<React.Fragment>
<PageSection id="page" variant={PageSectionVariants.light}>
<Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
<FlexItem spacer={{ default: 'spacerLg' }}>
<TextContent>
<Title headingLevel="h1">Tokens</Title>
</TextContent>
</FlexItem>
<FlexItem>
<Button
aria-label="Add token"
variant="secondary"
title="Add token"
onClick={() => setIsAddTokenOpen(true)}
>
<PlusCircleIcon /> Add Token
</Button>
</FlexItem>
</Flex>
</PageSection>
<PageSection>
<Card>
<CardBody className="pf-u-p-0">
<FilterTable
columns={COLUMNS}
rows={tokens.length > 0 ? tokens.map((t) => tokenToRow(t)) : [getSpinnerRow(4)]}
pagination={{
pageSize: pageSize,
page: page,
totalItems: totalItems
}}
isEmpty={tokens.length === 0}
isError={isError}
onSetPage={(_event, value) => {setPage(value);}}
onSetPageSize={(_event, value) => {setPageSize(value);}}
/>
</CardBody>
</Card>
</PageSection>
<AddTokenModal
isOpen={isAddTokenOpen}
onClose={() => setIsAddTokenOpen(false)}
/>
<DeleteModal
title="Delete token"
body="Would you like to delete the selected token?"
toDeleteId={tokenToDelete?.id}
toDeletePath={['user', 'token']}
isOpen={isDeleteTokenOpen}
onClose={() => {
setTokenToDelete();
setIsDeleteTokenOpen(false);
}}
/>
<ToastContainer autoClose={ALERT_TIMEOUT} stacked/>
</React.Fragment>
);
};

UserTokens.propTypes = {};

export default UserTokens;
27 changes: 9 additions & 18 deletions frontend/src/profile.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
import React from 'react';

import { Navigate, Route, Routes } from 'react-router-dom';
import EventEmitter from 'wolfy87-eventemitter';

import UserProfile from './pages/profile/user';
import { UserTokens } from './pages/profile/tokens';
import UserTokens from './pages/profile/tokens';
import './app.css';
import ElementWrapper from './components/elementWrapper';
import ProfilePage from './components/profile-page';


const Profile = () => {
// TODO useEffect instead of eventEmitter
const eventEmitter = new EventEmitter();

return (
<Routes>
<Route path="" element={<ProfilePage eventEmitter={eventEmitter}/>}>
<Route path="user" element={<UserProfile />} />
<Route path="tokens" element={<ElementWrapper routeElement={UserTokens} eventEmitter={eventEmitter} />} />
<Route path="*" element={<Navigate to="user" replace />}/>
</Route>
</Routes>
);
<Routes>
<Route path="" element={<ProfilePage />}>
<Route path="user" element={<UserProfile />} />
<Route path="tokens" element={<UserTokens />} />
<Route path="*" element={<Navigate to="user" replace />}/>
</Route>
</Routes>;
};

Profile.propTypes = {

};
Profile.propTypes = {};

export default Profile;

0 comments on commit bbbd416

Please sign in to comment.