Skip to content

Commit

Permalink
feat: app theming (#1120)
Browse files Browse the repository at this point in the history
## JIRA Ticket

[BSS-260](https://jira.csiro.au/browse/BSS-260): App Theming Setup
[BSS-134](https://jira.csiro.au/browse/BSS-134): Survey Page: Overall
page layout update
[BSS-217](https://jira.csiro.au/browse/BSS-217): Survey List Page: List
item styling
[BSS-215](https://jira.csiro.au/browse/BSS-215): Survey List Page: Top
bar styling

## Description

Set up the ability to support multiple themes and switch between them.
An example theme "bubble" is also included. Various components in the
notebook list page have been updated to use different settings between
the themes.

## Proposed Changes

- added theme environment variable
- moved theme files into default folder and created a theme file to
manage exports of theme specific code
- updated imports to use new theme exports
- added bubble theme files
- updated current components in the notebook list page to use theme
specific settings
- created survey card component
- created heading component

## How to Test

- log in to the app (if not already)
- navigate to the home page
- update `app/.env` to include `VITE_THEME=bubble` and save the file
- verify that the theme has changed
- (optional) navigate to a notebook page and verify that the theme has
changed there as well
- update `app/.env` to include `VITE_THEME=default` and save the file
- verify that the theme has changed back to the default theme

## Additional Information

Some features present in the default theme notebook list page have not
yet been implemented in the bubble theme.

## Checklist

- [x] I have confirmed all commits have been signed.
- [x] I have added JSDoc style comments to any new functions or classes.
- [x] Relevant documentation such as READMEs, guides, and class comments
are updated.
  • Loading branch information
luke-mcfarlane-rocketlab authored Sep 4, 2024
2 parents 246f325 + 8aa5671 commit f7b7aac
Show file tree
Hide file tree
Showing 24 changed files with 806 additions and 202 deletions.
20 changes: 10 additions & 10 deletions api/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,16 @@ import markdownit from 'markdown-it';
export const app = express();
app.use(morgan('combined'));

if (process.env.NODE_ENV !== 'test') {
// set up rate limiter: maximum of 30 requests per minute
const limiter = RateLimit({
windowMs: 1 * 60 * 1000, // 1 minute
max: 30,
validate: true,
});
app.use(limiter);
console.log('Rate limiter enabled');
}
// if (process.env.NODE_ENV !== 'test') {
// // set up rate limiter: maximum of 30 requests per minute
// const limiter = RateLimit({
// windowMs: 1 * 60 * 1000, // 1 minute
// max: 30,
// validate: true,
// });
// app.use(limiter);
// console.log('Rate limiter enabled');
// }

// Only parse query parameters into strings, not objects
app.set('query parser', 'simple');
Expand Down
2 changes: 2 additions & 0 deletions app/.env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ VITE_DEBUG_APP=true
VITE_PRODUCTION_BUILD=false
# Set to true for verbose debug messages from PouchDB
VITE_POUCHDB_DEBUG=false
# The theme to use for the app, 'default' or 'bubble'
VITE_THEME='default'
# Alternate views of notebook Active/Not Active lists: 'tabs' or 'headings'
VITE_NOTEBOOK_LIST_TYPE=tabs
# Configure the name used for Notebooks in the app, lowercase, singular (eg. notebook, survey)
Expand Down
2 changes: 1 addition & 1 deletion app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {ThemeProvider, StyledEngineProvider} from '@mui/material/styles';
// https://stackoverflow.com/a/64135466/3562777 temporary solution to remove findDOMNode is depreciated in StrictMode warning
// will be resolved in material-ui v5

import theme from './gui/theme';
import {theme} from './gui/themes';
import {getTokenContentsForCurrentUser} from './users';

import {useEffect, useState} from 'react';
Expand Down
9 changes: 9 additions & 0 deletions app/src/cross.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions app/src/gui/components/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
* Alerts are shown one at a time with configurable durations and severity levels.
*/

import React, {useContext} from 'react';
import {useContext} from 'react';
import Snackbar from '@mui/material/Snackbar';
import {ThemeProvider} from '@mui/material/styles';
import {theme} from '../themes';
import Alert from '@mui/material/Alert';
import {createUseStyles} from 'react-jss';
import theme from '../theme';
import {store} from '../../context/store';
import {ActionType} from '../../context/actions';

Expand Down
35 changes: 35 additions & 0 deletions app/src/gui/components/app-bar/app-bar-heading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {NavLink} from 'react-router-dom';
import {appBarHeading} from '../../themes/index';
import {NOTEBOOK_NAME_CAPITALIZED} from '../../../buildconfig';

interface AppBarHeadingProps {
link: string;
}

/**
* AppBarHeading component that conditionally renders a heading or a logo based on the appBarHeading value.
*
* @param {AppBarHeadingProps} props - The properties for the AppBarHeading component.
* @param {string} props.link - The link URL for the NavLink component.
* @returns {JSX.Element} - The rendered AppBarHeading component.
*/
export const AppBarHeading = ({link}: AppBarHeadingProps) =>
appBarHeading === 'bubble' ? (
<div
style={{
flexGrow: 1,
fontSize: 32,
fontWeight: 600,
textAlign: 'center',
}}
>
{NOTEBOOK_NAME_CAPITALIZED}s
</div>
) : (
<NavLink style={{flexGrow: 1}} to={link}>
<img
src="/static/logo/Fieldmark-Short-Green-NoBorder.png"
style={{maxWidth: '140px', flex: 1}}
/>
</NavLink>
);
124 changes: 69 additions & 55 deletions app/src/gui/components/ui/heading-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {Stack} from '@mui/material';
import {DataGrid, GridEventListener} from '@mui/x-data-grid';
import {ProjectInformation} from '@faims3/data-model/build/src/types';
import {NOTEBOOK_NAME, NOTEBOOK_NAME_CAPITALIZED} from '../../../buildconfig';
import ProjectCardList from './project-card-list';
import {projectListLayout} from '../../themes';

/**
* Renders a grid with two sections: Active and Not Active.
Expand Down Expand Up @@ -32,67 +34,79 @@ export default function HeadingGrid({
<div style={{padding: '6px', fontSize: '18px', fontWeight: 'bold'}}>
Active
</div>
<DataGrid
key={'active_notebook_list_datagrid'}
rows={pouchProjectList.filter(r => r.is_activated)}
loading={loading}
columns={columns}
onRowClick={handleRowClick}
autoHeight
sx={{cursor: 'pointer'}}
getRowId={r => r.project_id}
hideFooter={true}
getRowHeight={() => 'auto'}
initialState={{
sorting: {
sortModel: [sortModel],
},
pagination: {
paginationModel: {
pageSize: pouchProjectList.length,
{projectListLayout === 'card-list' ? (
<ProjectCardList
projects={pouchProjectList.filter(r => r.is_activated)}
/>
) : (
<DataGrid
key={'active_notebook_list_datagrid'}
rows={pouchProjectList.filter(r => r.is_activated)}
loading={loading}
columns={columns}
onRowClick={handleRowClick}
autoHeight
sx={{cursor: 'pointer'}}
getRowId={r => r.project_id}
hideFooter={true}
getRowHeight={() => 'auto'}
initialState={{
sorting: {
sortModel: [sortModel],
},
},
}}
slots={{
noRowsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center">
No {NOTEBOOK_NAME_CAPITALIZED}s have been activated yet.
</Stack>
),
}}
/>
pagination: {
paginationModel: {
pageSize: pouchProjectList.length,
},
},
}}
slots={{
noRowsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center">
No {NOTEBOOK_NAME_CAPITALIZED}s have been activated yet.
</Stack>
),
}}
/>
)}
<div style={{height: '16px'}} />
<div style={{padding: '6px', fontSize: '18px', fontWeight: 'bold'}}>
Not active
</div>
<DataGrid
key={'not_active_notebook_list_datagrid'}
rows={pouchProjectList.filter(r => !r.is_activated)}
loading={loading}
columns={columns}
autoHeight
sx={{cursor: 'pointer'}}
getRowId={r => r.project_id}
hideFooter={true}
getRowHeight={() => 'auto'}
initialState={{
sorting: {
sortModel: [sortModel],
},
pagination: {
paginationModel: {
pageSize: pouchProjectList.length,
{projectListLayout === 'card-list' ? (
<ProjectCardList
projects={pouchProjectList.filter(r => !r.is_activated)}
/>
) : (
<DataGrid
key={'not_active_notebook_list_datagrid'}
rows={pouchProjectList.filter(r => !r.is_activated)}
loading={loading}
columns={columns}
autoHeight
sx={{cursor: 'pointer'}}
getRowId={r => r.project_id}
hideFooter={true}
getRowHeight={() => 'auto'}
initialState={{
sorting: {
sortModel: [sortModel],
},
pagination: {
paginationModel: {
pageSize: pouchProjectList.length,
},
},
},
}}
slots={{
noRowsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center">
You don't have any unactivated {NOTEBOOK_NAME}s.
</Stack>
),
}}
/>
}}
slots={{
noRowsOverlay: () => (
<Stack height="100%" alignItems="center" justifyContent="center">
You don't have any unactivated {NOTEBOOK_NAME}s.
</Stack>
),
}}
/>
)}
</div>
);
}
40 changes: 40 additions & 0 deletions app/src/gui/components/ui/project-card-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {ProjectInformation} from '@faims3/data-model';
import ProjectCard from './project-card';
import {useNavigate} from 'react-router-dom';
import * as ROUTES from '../../../constants/routes';

/**
* ProjectCardList component that displays a list of ProjectCard components.
*
* @param {ProjectCardListProps} props - The properties for the ProjectCardList component.
* @param {ProjectInformation[]} props.projects - An array of project objects containing details to be displayed in the list.
* @returns {JSX.Element} - The rendered ProjectCardList component.
*/
export default function ProjectCardList({
projects,
}: {
projects: ProjectInformation[];
}) {
const navigate = useNavigate();

const onClick = (project_id: string, activated: boolean) =>
activated && navigate(ROUTES.INDIVIDUAL_NOTEBOOK_ROUTE + project_id);

return (
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 16,
}}
>
{projects.map(project => (
<ProjectCard
key={project.project_id}
project={project}
onClick={onClick}
/>
))}
</div>
);
}
Loading

0 comments on commit f7b7aac

Please sign in to comment.