Skip to content

Commit

Permalink
Tweaks: Projects layout, Vite upgrade; replace react-table with expli…
Browse files Browse the repository at this point in the history
…cit markup (#118)

* styling tweaks; vite upgrade; replace react-table with explicit markup

* fix tests
  • Loading branch information
jarosenb authored Jan 30, 2023
1 parent 941c9c1 commit 540fbf0
Show file tree
Hide file tree
Showing 22 changed files with 1,313 additions and 830 deletions.
6 changes: 2 additions & 4 deletions apps/tup-ui/src/pages/Projects/Projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import {
ProjectsNavbar,
ProjectsListing,
} from '@tacc/tup-components';
import styles from './Projects.module.css';
import { SectionHeader } from '@tacc/core-components';

const Layout: React.FC = () => {
return (
<RequireAuth>
<PageLayout
top={
<div className={styles['project-header']}>Projects & Allocations</div>
}
top={<SectionHeader>Projects & Allocations</SectionHeader>}
left={<ProjectsNavbar />}
right={<ProjectsListing />}
></PageLayout>
Expand Down
2 changes: 1 addition & 1 deletion apps/tup-ui/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineConfig, PluginOption } from 'vite';
import react from '@vitejs/plugin-react';
import react from '@vitejs/plugin-react-swc';
import path from 'path';

function localFsPlugin(): PluginOption {
Expand Down
2 changes: 1 addition & 1 deletion libs/tup-components/src/news/UserNews.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const UserNews: React.FC = () => {
<ul className={styles['news-list']}>
{data &&
data.slice(0, 12).map((newsItem) => (
<li className={styles['news-list-item']}>
<li className={styles['news-list-item']} key={newsItem.ID}>
<div className={styles['posted-date']}>
<span>Posted {formatDate(newsItem.PostedDate)} </span>
{newsItem.Updates && (
Expand Down
2 changes: 1 addition & 1 deletion libs/tup-components/src/projects/ProjectLayout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('Projects Layout Component', () => {
await waitFor(() => getAllByRole('columnheader'));
const columnHeaders: HTMLElement[] = getAllByRole('columnheader');
expect(columnHeaders[0].textContent).toEqual('Project Title');
expect(columnHeaders[1].textContent).toEqual('Principle Investigator');
expect(columnHeaders[1].textContent).toEqual('Principal Investigator');
expect(columnHeaders[2].textContent).toEqual('Active Allocations');
expect(getByText('JAR TUP Development Project')).toBeDefined();
expect(getByText('Jake Rosenberg')).toBeDefined();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('Projects Summary Listing Component', () => {
await screen.findByText('JAR TUP Development Project');
expect(screen.findAllByText('/projects/59184', { exact: false }));
expect(screen.findAllByText('Project Charge Code: STA22002'));
expect(screen.findAllByText('Principle Investigator: Jake Rosenberg'));
expect(screen.findAllByText('Principal Investigator: Jake Rosenberg'));
expect(screen.getAllByText('10 SUs'));
expect(screen.getAllByText('(0% Used)'));
expect(screen.getAllByText('-- GBs'));
Expand Down
2 changes: 1 addition & 1 deletion libs/tup-components/src/projects/ProjectsTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('Projects Table Component', () => {
await waitFor(() => getAllByRole('columnheader'));
const columnHeaders: HTMLElement[] = getAllByRole('columnheader');
expect(columnHeaders[0].textContent).toEqual('Project Title');
expect(columnHeaders[1].textContent).toEqual('Principle Investigator');
expect(columnHeaders[1].textContent).toEqual('Principal Investigator');
expect(columnHeaders[2].textContent).toEqual('Active Allocations');
expect(getByText('JAR TUP Development Project')).toBeDefined();
expect(getByText('Jake Rosenberg')).toBeDefined();
Expand Down
82 changes: 31 additions & 51 deletions libs/tup-components/src/projects/ProjectsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,17 @@
import React, { useMemo } from 'react';
import { useTable, Column } from 'react-table';
import React from 'react';
import { LoadingSpinner, InlineMessage } from '@tacc/core-components';
import { ProjectTitle, PrinInv, Allocations } from './ProjectsCells';
import { ProjectsRawSystem, useProjects } from '@tacc/tup-hooks';
import { ProjectsAllocations, useProjects } from '@tacc/tup-hooks';
import { Link } from 'react-router-dom';

const allocationDisplay = (allocations: ProjectsAllocations[]) => {
return allocations.length
? Array.from(new Set(allocations.map((e) => e.resource))).join(', ')
: '--';
};

export const ProjectsTable: React.FC = () => {
const { data, isLoading, error } = useProjects();
const columns = useMemo<Column<ProjectsRawSystem>[]>(
() => [
{
accessor: 'title',
Header: 'Project Title',
Cell: ProjectTitle,
},
{
accessor: ({ pi }) => pi.firstName + ' ' + pi.lastName,
Header: 'Principle Investigator',
// Cell: PrinInv,
},
{
accessor: ({ allocations }) =>
allocations ? allocations.map((e) => e.resource).join(', ') : '--',
Header: 'Active Allocations',
// Cell: Allocations,
},
],
[]
);
const { getTableProps, getTableBodyProps, rows, prepareRow, headerGroups } =
useTable({
columns,
data: data ?? [],
});

if (isLoading) {
return <LoadingSpinner />;
}
Expand All @@ -41,31 +22,30 @@ export const ProjectsTable: React.FC = () => {
}
return (
<div className="o-fixed-header-table">
<table {...getTableProps()}>
<table style={{ width: '100%' }}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
))}
</tr>
))}
<tr>
<th>Project Title</th>
<th>Principal Investigator</th>
<th>Active Allocations</th>
</tr>
</thead>
<tbody {...getTableBodyProps()}>
{rows.length ? (
rows.map((row, idx) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => (
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
))}
</tr>
);
})
<tbody>
{data && data.length ? (
data.map((project) => (
<tr key={project.id}>
<td style={{ width: '30%' }}>
<Link to={`/projects/${project.id}`}>{project.title}</Link>
</td>
<td
style={{ width: '20%' }}
>{`${project.pi.firstName} ${project.pi.lastName}`}</td>
<td>{allocationDisplay(project.allocations ?? [])}</td>
</tr>
))
) : (
<tr>
<td colSpan={5}>No active projects found.</td>
<td colSpan={3}>No active projects found.</td>
</tr>
)}
</tbody>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Link, useParams } from 'react-router-dom';
import { useProjects, useProjectScienceField } from '@tacc/tup-hooks';
import styles from './Projects.module.css';
import {
Expand All @@ -11,6 +11,8 @@ import {
export const ProjectHeader: React.FC<{ projectId: number }> = ({
projectId,
}) => {
const params = useParams<{ projectId: string; username: string }>();

const { data, isLoading, error } = useProjects();
const fieldData = useProjectScienceField();
const dataById = data?.find((project) => project.id === projectId);
Expand Down Expand Up @@ -62,7 +64,13 @@ export const ProjectHeader: React.FC<{ projectId: number }> = ({
{!isActive && (
<Link to={'/projects?show=inactive'}>Inactive Projects </Link>
)}
{`/ ${dataById?.title}`}
{!params.username && `/ ${dataById?.title}`}
{params.username && (
<>
<Link to={`/projects/${projectId}`}>{`/ ${dataById?.title}`} </Link>
{`/ ${params.username}`}{' '}
</>
)}
</h3>
<div className={styles['separator']}></div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@
}
.separator-bottom {
border-bottom: 1px solid #C7C7C7;
margin: .5em;
margin-top: .5em;
margin-left: .5em;
margin-right: .5em;

}
.loading-placeholder {
height: 100%;
width: 175px;
min-height: 107px;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.user-detail-container {
width: 50%;
margin-left: -25px;
margin-top: -20px;
}

.user-info-banner {
Expand Down
95 changes: 88 additions & 7 deletions libs/tup-components/src/projects/users/UserDetail/UserDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,97 @@
import { LoadingSpinner } from '@tacc/core-components';
import { LoadingSpinner, Button } from '@tacc/core-components';
import {
useProjectUsers,
useProjectUsage,
UsagePerResource,
ProjectUser,
useProfile,
} from '@tacc/tup-hooks';
import { useState } from 'react';
import styles from './UserDetail.module.css';

const UserRoleSelector: React.FC = () => (
<div>(User role selector placeholder)</div>
);
const UserRoleSelector: React.FC<{ projectId: number; user: ProjectUser }> = ({
user,
projectId,
}) => {
const { data: currentUser } = useProfile();
const projectUsers = useProjectUsers(projectId);
const projectCurrentUser = (projectUsers.data ?? []).find(
(user) => user.username === currentUser?.username
);
const [confirmState, setConfirmState] = useState<'DEFAULT' | 'CONFIRM'>(
'DEFAULT'
);
const mutate = () => {
console.log('setting delegate');
};

const canSetDelegate =
user.role === 'Standard' && projectCurrentUser?.role === 'PI';
return (
<div>
<strong>Role:</strong> {user.role}{' '}
{canSetDelegate && confirmState === 'DEFAULT' && (
<Button onClick={() => setConfirmState('CONFIRM')} type="link">
Set as Project Delegate
</Button>
)}
{canSetDelegate && confirmState === 'CONFIRM' && (
<>
<Button onClick={() => mutate()} type="link">
Confirm Delegate Selection
</Button>{' '}
|{' '}
<Button onClick={() => setConfirmState('DEFAULT')} type="link">
Cancel
</Button>
</>
)}
</div>
);
};

const RemoveUser: React.FC<{ projectId: number; user: ProjectUser }> = ({
projectId,
user,
}) => {
const { data: currentUser } = useProfile();
const projectUsers = useProjectUsers(projectId);
const projectCurrentUser = (projectUsers.data ?? []).find(
(user) => user.username === currentUser?.username
);
const [confirmState, setConfirmState] = useState<'DEFAULT' | 'CONFIRM'>(
'DEFAULT'
);

const mutate = () => {
console.log('remove user');
};

if (!['PI', 'Delegate'].includes(projectCurrentUser?.role ?? '')) return null;
if (user.role === 'PI') return null;
if (user.username === currentUser?.username) return null;

const RemoveUser: React.FC = () => <div>(User removal placeholder)</div>;
if (confirmState === 'DEFAULT')
return (
<Button onClick={() => setConfirmState('CONFIRM')} type="link">
Remove User
</Button>
);
if (confirmState === 'CONFIRM')
return (
<>
<Button onClick={() => mutate()} type="link">
Remove
</Button>{' '}
|{' '}
<Button onClick={() => setConfirmState('DEFAULT')} type="link">
Cancel
</Button>
</>
);

return <div>(User removal placeholder)</div>;
};

const UsageTable: React.FC<{ projectId: number; username: string }> = ({
projectId,
Expand Down Expand Up @@ -85,10 +166,10 @@ const UserDetail: React.FC<{ projectId: number; username: string }> = ({
</div>
<div className={styles['manage-user-container']}>
<div>
<UserRoleSelector />
<UserRoleSelector projectId={projectId} user={user} />
</div>
<div>
<RemoveUser />
<RemoveUser projectId={projectId} user={user} />
</div>
</div>
<UsageTable username={username} projectId={projectId} />
Expand Down
57 changes: 57 additions & 0 deletions libs/tup-components/src/projects/users/UserList/ManageTeam.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { InlineMessage, LoadingSpinner } from '@tacc/core-components';
import { useProfile, useProjectUsers } from '@tacc/tup-hooks';
import React from 'react';
import { Input, InputGroup, Button, InputGroupAddon } from 'reactstrap';
import styles from './UserList.module.css';

const ManageTeam: React.FC<{ projectId: number }> = ({ projectId }) => {
const { data: currentUser } = useProfile();
const { data: users } = useProjectUsers(projectId);
const currentUserRole =
(users ?? []).find((user) => user.username === currentUser?.username)
?.role || '';

if (!['PI', 'Delegate'].includes(currentUserRole)) return null;

const addUser = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
e.stopPropagation();
console.log('submitting');
};

return (
<>
<div style={{ paddingTop: '10px', marginRight: '10px' }}>
<div style={{ paddingBottom: '16px' }}>
<span style={{ fontSize: '1.5rem' }}>
<strong>Manage Team</strong>
</span>{' '}
({users?.length} Users)
</div>
<form onSubmit={(e) => addUser(e)} style={{ paddingBottom: '16px' }}>
<label htmlFor="add-user">Add New User</label>
<InputGroup>
<InputGroupAddon addonType="prepend">
<Button
type="submit"
style={{
borderColor: '#AFAFAF',
backgroundColor: '#F4F4F4',
borderRadius: '0',
color: '#484848',
}}
>
Add <LoadingSpinner placement="inline"></LoadingSpinner>
</Button>
</InputGroupAddon>
<Input placeholder="Enter Username" id="add-user" />
</InputGroup>
<InlineMessage type="error">User not found.</InlineMessage>
</form>
</div>
<div className={styles['separator']}></div>
</>
);
};

export default ManageTeam;
Loading

0 comments on commit 540fbf0

Please sign in to comment.