Updating
This commit is contained in:
3
app/src/components/app/App.module.scss
Normal file
3
app/src/components/app/App.module.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.content {
|
||||
padding: 30px;
|
||||
}
|
||||
@@ -1,38 +1,44 @@
|
||||
import React from 'react';
|
||||
import { Routes, Route, useLocation } from 'react-router-dom';
|
||||
import { Container, Row, Col } from 'react-grid-system';
|
||||
import styles from './App.module.scss'
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import { initializeIcons } from '@fluentui/font-icons-mdl2';
|
||||
import Menu from '../menu/Menu';
|
||||
import { Grid, Sidebar } from 'semantic-ui-react';
|
||||
import Header from '../header/Header';
|
||||
import Footer from '../footer/Footer';
|
||||
import Navigation from '../navigation/Navigation';
|
||||
import Teams from '../teams/Teams';
|
||||
import Venues from '../venues/Venues';
|
||||
import { useSidebarContext } from '../../hooks/sidebarContext/UseSidebarContext';
|
||||
|
||||
initializeIcons();
|
||||
|
||||
const App: React.FC<unknown> = () => {
|
||||
const location = useLocation();
|
||||
const sidebarContext = useSidebarContext();
|
||||
|
||||
return (
|
||||
<Container fluid={true} style={{ padding: '0' }}>
|
||||
<Row>
|
||||
<Col sm={6}>Goodbye!</Col>
|
||||
<Col sm={6}>Hello!</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col sm={3}>
|
||||
<Menu
|
||||
selectedKey={
|
||||
location.pathname === '/'
|
||||
? 'home'
|
||||
: location.pathname.replace('/', '')
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col>
|
||||
<Routes>
|
||||
<Route path='venues' element={<Venues />} />
|
||||
</Routes>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
<Sidebar.Pushable>
|
||||
<Sidebar.Pusher dimmed={sidebarContext.state.sidebar?.visible}>
|
||||
<Header />
|
||||
<Grid columns={16}>
|
||||
<Grid.Row>
|
||||
<Grid.Column width={3}>
|
||||
<Navigation />
|
||||
</Grid.Column>
|
||||
<Grid.Column width={12}>
|
||||
<div className={styles.content}>
|
||||
<Routes>
|
||||
<Route path='teams' element={<Teams />} />
|
||||
</Routes>
|
||||
<Routes>
|
||||
<Route path='venues' element={<Venues />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
<Footer />
|
||||
</Sidebar.Pusher>
|
||||
</Sidebar.Pushable>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
4
app/src/components/footer/Footer.module.scss
Normal file
4
app/src/components/footer/Footer.module.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
.siteFooter {
|
||||
background-color: #e0e1e2;
|
||||
height: 150px;
|
||||
}
|
||||
22
app/src/components/footer/Footer.tsx
Normal file
22
app/src/components/footer/Footer.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import styles from './Footer.module.scss';
|
||||
import { Grid } from 'semantic-ui-react';
|
||||
|
||||
const Footer: React.FC<unknown> = () => {
|
||||
return (
|
||||
<div className={styles.siteFooter}>
|
||||
<Grid columns={16}>
|
||||
<Grid.Row>
|
||||
<Grid.Column width={8}>
|
||||
Hello!
|
||||
</Grid.Column>
|
||||
<Grid.Column width={8} textAlign='right'>
|
||||
Goodbye!
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
4
app/src/components/header/Header.module.scss
Normal file
4
app/src/components/header/Header.module.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
.siteHeader {
|
||||
background-color: #e0e1e2;
|
||||
height: 100px;
|
||||
}
|
||||
22
app/src/components/header/Header.tsx
Normal file
22
app/src/components/header/Header.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import styles from './Header.module.scss';
|
||||
import { Grid, Icon } from 'semantic-ui-react';
|
||||
|
||||
const Header: React.FC<unknown> = () => {
|
||||
return (
|
||||
<div className={styles.siteHeader}>
|
||||
<Grid columns={16}>
|
||||
<Grid.Row>
|
||||
<Grid.Column width={8}>
|
||||
<Icon name='baseball ball' size='huge' />
|
||||
</Grid.Column>
|
||||
<Grid.Column width={8} textAlign='right'>
|
||||
Goodbye!
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header;
|
||||
@@ -1,3 +0,0 @@
|
||||
export interface IMenuProps {
|
||||
selectedKey: string;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import React from 'react';
|
||||
import { IMenuProps } from './IMenuProps';
|
||||
import {
|
||||
Nav,
|
||||
INavLinkGroup
|
||||
} from '@fluentui/react/lib/Nav';
|
||||
|
||||
const Menu: React.FC<IMenuProps> = (props: IMenuProps) => {
|
||||
const navLinkGroups: INavLinkGroup[] = [
|
||||
{
|
||||
links: [
|
||||
{
|
||||
name: 'Home',
|
||||
url: 'http://localhost:3000',
|
||||
key: 'home'
|
||||
},
|
||||
{
|
||||
name: 'Venues',
|
||||
url: 'http://localhost:3000/venues',
|
||||
key: 'venues'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
return <Nav groups={navLinkGroups} selectedKey={props.selectedKey} />;
|
||||
};
|
||||
|
||||
export default Menu;
|
||||
3
app/src/components/navigation/INavigationState.ts
Normal file
3
app/src/components/navigation/INavigationState.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface INavigationState {
|
||||
activeLink: string;
|
||||
}
|
||||
75
app/src/components/navigation/Navigation.tsx
Normal file
75
app/src/components/navigation/Navigation.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React, { Component, useEffect, useReducer } from 'react';
|
||||
// import { IMenuProps } from './IMenuProps';
|
||||
// import {
|
||||
// Nav,
|
||||
// INavLinkGroup
|
||||
// } from '@fluentui/react/lib/Nav';
|
||||
import { Menu } from 'semantic-ui-react';
|
||||
import { initialState, reducer } from './reducer';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
// const Menu: React.FC<IMenuProps> = (props: IMenuProps) => {
|
||||
// const navLinkGroups: INavLinkGroup[] = [
|
||||
// {
|
||||
// links: [
|
||||
// {
|
||||
// name: 'Home',
|
||||
// url: 'http://localhost:3000',
|
||||
// key: 'home'
|
||||
// },
|
||||
// {
|
||||
// name: 'Venues',
|
||||
// url: 'http://localhost:3000/venues',
|
||||
// key: 'venues'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ];
|
||||
|
||||
// return <Nav groups={navLinkGroups} selectedKey={props.selectedKey} />;
|
||||
// };
|
||||
|
||||
// export default Menu;
|
||||
|
||||
const Navigation: React.FC<unknown> = (props) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const menuItems: string[] = [
|
||||
'gameday',
|
||||
'teams',
|
||||
'venues'
|
||||
];
|
||||
|
||||
const onMenuItemClicked = (event: any, { name }: any) => {
|
||||
navigate(name);
|
||||
dispatch({ type: 'SET_ACTIVE_MENU_ITEM', payload: name });
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const activeLink: string = location.pathname === '/' ? 'home' : location.pathname.replace('/', '')
|
||||
|
||||
dispatch({ type: 'SET_ACTIVE_MENU_ITEM', payload: activeLink });
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Menu
|
||||
pointing
|
||||
secondary
|
||||
vertical
|
||||
>
|
||||
{menuItems.map((menuItem: string) => {
|
||||
return (
|
||||
<Menu.Item
|
||||
key={menuItem}
|
||||
name={menuItem}
|
||||
active={state.activeLink === menuItem}
|
||||
onClick={onMenuItemClicked}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
export default Navigation;
|
||||
19
app/src/components/navigation/reducer.ts
Normal file
19
app/src/components/navigation/reducer.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { INavigationState } from "./INavigationState"
|
||||
|
||||
export type Action =
|
||||
| { type: 'SET_ACTIVE_MENU_ITEM', payload: string }
|
||||
|
||||
export const initialState: INavigationState = {
|
||||
activeLink: ''
|
||||
}
|
||||
|
||||
export const reducer = (state: INavigationState, action: Action): INavigationState => {
|
||||
switch (action.type) {
|
||||
case 'SET_ACTIVE_MENU_ITEM': {
|
||||
return {
|
||||
...state,
|
||||
activeLink: action.payload
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
app/src/components/teams/ITeamsState.ts
Normal file
13
app/src/components/teams/ITeamsState.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ICommandBarItemProps } from "@fluentui/react/lib/CommandBar";
|
||||
import { ITeam } from '../../models/ITeam';
|
||||
|
||||
export interface ITeamsState {
|
||||
items: ITeam[];
|
||||
totalItemCount: number | null;
|
||||
totalPageCount: number | null;
|
||||
currentPageNumber: number;
|
||||
pageSize: number;
|
||||
selectedItems: ITeam[];
|
||||
commandBarItems: ICommandBarItemProps[];
|
||||
isPanelOpen: boolean;
|
||||
}
|
||||
135
app/src/components/teams/Teams.tsx
Normal file
135
app/src/components/teams/Teams.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
import React, { SyntheticEvent, useEffect, useReducer } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { MessageBar, MessageBarType } from '@fluentui/react/lib/MessageBar';
|
||||
import { initialState, reducer } from './reducer'
|
||||
import { useAppContext } from '../../hooks/appContext/UseAppContext';
|
||||
import { useSidebarContext } from '../../hooks/sidebarContext/UseSidebarContext';
|
||||
import { GET_TEAMS } from '../../constants/graphQL';
|
||||
import { INotification } from '../../models/INotification';
|
||||
import { Button, Grid, Header, Icon, Loader, Pagination, Table } from 'semantic-ui-react'
|
||||
|
||||
const Teams: React.FC<unknown> = () => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const appContext = useAppContext();
|
||||
const sidebarContext = useSidebarContext();
|
||||
const currentPageNumber = state.currentPageNumber;
|
||||
const pageSize = state.pageSize;
|
||||
const { data, loading, error, refetch } = useQuery(GET_TEAMS, {
|
||||
variables: {
|
||||
pageNumber: currentPageNumber,
|
||||
pageSize: pageSize
|
||||
}
|
||||
})
|
||||
|
||||
const columns: string[] = [
|
||||
'Name',
|
||||
''
|
||||
]
|
||||
|
||||
const onPageChange = (event: SyntheticEvent, data: any) => {
|
||||
const newCurrentPage: number = data.activePage;
|
||||
|
||||
dispatch({ type: 'SET_CURRENT_PAGE_NUMBER', payload: newCurrentPage });
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
dispatch({
|
||||
type: 'SET_ITEMS',
|
||||
payload: {
|
||||
items: data.teams.results,
|
||||
totalItemCount: data.teams.totalItemCount,
|
||||
totalPageCount: data.teams.totalPageCount
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [data])
|
||||
|
||||
useEffect(() => {
|
||||
refetch({
|
||||
pageNumber: currentPageNumber,
|
||||
pageSize: pageSize
|
||||
})
|
||||
}, [currentPageNumber])
|
||||
|
||||
return (
|
||||
<Grid columns={1}>
|
||||
<Grid.Row>
|
||||
<Header as='h1'>
|
||||
Teams
|
||||
</Header>
|
||||
</Grid.Row>
|
||||
{appContext.state.notification?.isVisible && (
|
||||
<Grid.Row>
|
||||
<Grid.Column>
|
||||
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
)}
|
||||
<Grid.Row>
|
||||
<Grid.Column>
|
||||
{loading && (
|
||||
<Loader size='massive' active={loading}>Loading...</Loader>
|
||||
)}
|
||||
{!loading &&
|
||||
<Table selectable>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
{columns.map((column: string) => {
|
||||
return (
|
||||
<Table.HeaderCell>
|
||||
{column}
|
||||
</Table.HeaderCell>
|
||||
)
|
||||
})}
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{state.items.map((item) => {
|
||||
return (
|
||||
<Table.Row
|
||||
key={item.id}
|
||||
>
|
||||
<Table.Cell>
|
||||
{item.name}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign='right'>
|
||||
<Button
|
||||
basic
|
||||
icon
|
||||
// onClick={() => onDetails(item.id)}
|
||||
>
|
||||
<Icon name='info circle' />
|
||||
</Button>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)
|
||||
})}
|
||||
</Table.Body>
|
||||
<Table.Footer>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan='2'>
|
||||
<Grid columns={1}>
|
||||
<Grid.Row>
|
||||
<Grid.Column textAlign='right'>
|
||||
<Pagination
|
||||
totalPages={data.teams.totalPageCount}
|
||||
activePage={state.currentPageNumber}
|
||||
onPageChange={onPageChange}
|
||||
size='mini'
|
||||
/>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Footer>
|
||||
</Table>
|
||||
}
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default Teams;
|
||||
73
app/src/components/teams/reducer.ts
Normal file
73
app/src/components/teams/reducer.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { ITeamsState } from './ITeamsState';
|
||||
import { ITeam } from '../../models/ITeam';
|
||||
import { ICommandBarItemProps } from '@fluentui/react/lib/CommandBar';
|
||||
import { IObjectWithKey } from '@fluentui/react';
|
||||
|
||||
export type Action =
|
||||
| {
|
||||
type: 'SET_ITEMS';
|
||||
payload: {
|
||||
items: ITeam[];
|
||||
totalItemCount: number;
|
||||
totalPageCount: number;
|
||||
};
|
||||
}
|
||||
| { type: 'SET_CURRENT_PAGE_NUMBER'; payload: number }
|
||||
| { type: 'SET_PAGE_SIZE'; payload: number }
|
||||
| { type: 'SET_SELECTED_ITEM'; payload: IObjectWithKey[] }
|
||||
| { type: 'SET_COMMAND_BAR_ITEMS'; payload: ICommandBarItemProps[] }
|
||||
| { type: 'SET_IS_PANEL_OPEN'; payload: boolean };
|
||||
|
||||
export const initialState: ITeamsState = {
|
||||
items: [],
|
||||
totalItemCount: null,
|
||||
totalPageCount: null,
|
||||
currentPageNumber: 1,
|
||||
pageSize: 10,
|
||||
selectedItems: [],
|
||||
commandBarItems: [],
|
||||
isPanelOpen: false
|
||||
}
|
||||
|
||||
export const reducer = (state: ITeamsState, action: Action): ITeamsState => {
|
||||
switch (action.type) {
|
||||
case 'SET_ITEMS': {
|
||||
return {
|
||||
...state,
|
||||
items: action.payload.items,
|
||||
totalItemCount: action.payload.totalItemCount,
|
||||
totalPageCount: action.payload.totalPageCount
|
||||
};
|
||||
}
|
||||
case 'SET_CURRENT_PAGE_NUMBER': {
|
||||
return {
|
||||
...state,
|
||||
currentPageNumber: action.payload
|
||||
};
|
||||
}
|
||||
case 'SET_PAGE_SIZE': {
|
||||
return {
|
||||
...state,
|
||||
pageSize: action.payload
|
||||
};
|
||||
}
|
||||
case 'SET_SELECTED_ITEM': {
|
||||
return {
|
||||
...state,
|
||||
selectedItems: <ITeam[]>action.payload
|
||||
};
|
||||
}
|
||||
case 'SET_COMMAND_BAR_ITEMS': {
|
||||
return {
|
||||
...state,
|
||||
commandBarItems: action.payload
|
||||
};
|
||||
}
|
||||
case 'SET_IS_PANEL_OPEN': {
|
||||
return {
|
||||
...state,
|
||||
isPanelOpen: action.payload
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
3
app/src/components/venueDetails/IVenueDetailsProps.ts
Normal file
3
app/src/components/venueDetails/IVenueDetailsProps.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface IVenueDetailsProps {
|
||||
venueId: number;
|
||||
}
|
||||
33
app/src/components/venueDetails/VenueDetails.tsx
Normal file
33
app/src/components/venueDetails/VenueDetails.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { IVenueDetailsProps } from './IVenueDetailsProps';
|
||||
import { Grid, Header } from 'semantic-ui-react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { GET_VENUE_BY_ID } from '../../constants/graphQL';
|
||||
|
||||
const VenueDetails: React.FC<IVenueDetailsProps> = ({ venueId }: IVenueDetailsProps) => {
|
||||
const { data, loading, error } = useQuery(GET_VENUE_BY_ID, {
|
||||
variables: {
|
||||
id: venueId
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Header as='h2'>
|
||||
Venue
|
||||
</Header>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
{data &&
|
||||
<ul>
|
||||
<li>{data.venueById.results.name}</li>
|
||||
<li>{data.venueById.results.id}</li>
|
||||
</ul>
|
||||
}
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
|
||||
export default VenueDetails;
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import AppContextProvider from '../../context/AppContextProvider';
|
||||
import AppContextProvider from '../../context/app/AppContextProvider';
|
||||
import Venues from './Venues';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { GET_VENUES } from '../../constants/graphQL';
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
import React, { useEffect, useReducer } from 'react';
|
||||
import React, { SyntheticEvent, useEffect, useReducer } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import {
|
||||
CommandBar,
|
||||
ICommandBarItemProps
|
||||
} from '@fluentui/react/lib/CommandBar';
|
||||
import {
|
||||
DetailsList,
|
||||
IColumn,
|
||||
Selection,
|
||||
SelectionMode
|
||||
} from '@fluentui/react/lib/DetailsList';
|
||||
import { MessageBar, MessageBarType } from '@fluentui/react/lib/MessageBar';
|
||||
import { Panel } from '@fluentui/react/lib/Panel';
|
||||
import Paginator from '../paginator/Paginator';
|
||||
import { Spinner, SpinnerSize } from '@fluentui/react/lib/Spinner';
|
||||
import { initialState, reducer } from './reducer';
|
||||
import { useAppContext } from '../../hooks/appContext/UseAppContext';
|
||||
import { useSidebarContext } from '../../hooks/sidebarContext/UseSidebarContext';
|
||||
import { GET_VENUES } from '../../constants/graphQL';
|
||||
import { INotification } from '../../models/INotification';
|
||||
import { IVenue } from '../../models/IVenue';
|
||||
import { Button, Grid, Header, Icon, Loader, Pagination, Sidebar, Table } from 'semantic-ui-react';
|
||||
import VenueDetails from '../venueDetails/VenueDetails';
|
||||
|
||||
const Venues: React.FC<unknown> = () => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const appContext = useAppContext();
|
||||
const sidebarContext = useSidebarContext();
|
||||
const currentPageNumber = state.currentPageNumber;
|
||||
const pageSize = state.pageSize;
|
||||
const { data, loading, error, refetch } = useQuery(GET_VENUES, {
|
||||
@@ -31,24 +21,11 @@ const Venues: React.FC<unknown> = () => {
|
||||
pageSize: pageSize
|
||||
}
|
||||
});
|
||||
const columns: IColumn[] = [
|
||||
{
|
||||
key: 'name',
|
||||
name: 'Name',
|
||||
fieldName: 'name',
|
||||
minWidth: 100,
|
||||
isResizable: true
|
||||
}
|
||||
];
|
||||
|
||||
const selection: Selection = new Selection({
|
||||
onSelectionChanged: () =>
|
||||
dispatch({ type: 'SET_SELECTED_ITEM', payload: selection.getSelection() })
|
||||
});
|
||||
|
||||
const onIsPanelOpen = () => {
|
||||
dispatch({ type: 'SET_IS_PANEL_OPEN', payload: !state.isPanelOpen });
|
||||
};
|
||||
const columns: string[] = [
|
||||
'Name',
|
||||
''
|
||||
]
|
||||
|
||||
const onDismissMessageBar = () => {
|
||||
const notification: INotification = {
|
||||
@@ -59,32 +36,21 @@ const Venues: React.FC<unknown> = () => {
|
||||
appContext.dispatch({ type: 'SET_NOTIFICATION', payload: notification });
|
||||
};
|
||||
|
||||
const onPageChange = (newCurrentPage: number) => {
|
||||
console.log(newCurrentPage);
|
||||
const onPageChange = (event: SyntheticEvent, data: any) => {
|
||||
const newCurrentPage: number = data.activePage;
|
||||
|
||||
dispatch({ type: 'SET_CURRENT_PAGE_NUMBER', payload: newCurrentPage });
|
||||
refetch({
|
||||
pageNumber: newCurrentPage,
|
||||
pageSize: pageSize
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: 'SET_COMMAND_BAR_ITEMS',
|
||||
payload: [
|
||||
{
|
||||
key: 'openItem',
|
||||
text: 'Open',
|
||||
iconProps: { iconName: 'OpenPane' },
|
||||
onClick: onIsPanelOpen,
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
});
|
||||
}, []);
|
||||
const onDetails = (id: number) => {
|
||||
sidebarContext.dispatch({ type: 'SET_SIDEBAR', payload: { sidebar: { visible: true, animation: 'overlay', width: 'very wide', direction: 'right' }, content: <VenueDetails venueId={id} />}})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log(sidebarContext)
|
||||
}, [sidebarContext])
|
||||
|
||||
useEffect(() => {
|
||||
console.log(JSON.stringify(data));
|
||||
if (data) {
|
||||
dispatch({
|
||||
type: 'SET_ITEMS',
|
||||
@@ -98,6 +64,7 @@ const Venues: React.FC<unknown> = () => {
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(error);
|
||||
const notification: INotification = {
|
||||
messageBarType: MessageBarType.error,
|
||||
message: error?.message,
|
||||
@@ -109,68 +76,97 @@ const Venues: React.FC<unknown> = () => {
|
||||
}, [error]);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.selectedItems.length > 0 && state.commandBarItems.length > 0) {
|
||||
const newCommandBarItems = state.commandBarItems.map(
|
||||
(commandBarItem: ICommandBarItemProps) => {
|
||||
return {
|
||||
...commandBarItem,
|
||||
disabled: false
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
dispatch({ type: 'SET_COMMAND_BAR_ITEMS', payload: newCommandBarItems });
|
||||
}
|
||||
}, [state.selectedItems]);
|
||||
refetch({
|
||||
pageNumber: currentPageNumber,
|
||||
pageSize: pageSize
|
||||
});
|
||||
}, [currentPageNumber])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{loading && <Spinner size={SpinnerSize.large} />}
|
||||
{!loading && (
|
||||
<React.Fragment>
|
||||
{appContext.state.notification?.isVisible && (
|
||||
<MessageBar
|
||||
messageBarType={appContext.state.notification?.messageBarType}
|
||||
isMultiline={appContext.state.notification?.isMultiline}
|
||||
onDismiss={onDismissMessageBar}
|
||||
>
|
||||
{appContext.state.notification?.message}
|
||||
</MessageBar>
|
||||
)}
|
||||
<CommandBar items={state.commandBarItems} />
|
||||
<DetailsList
|
||||
columns={columns}
|
||||
items={state.items}
|
||||
selection={selection}
|
||||
selectionMode={SelectionMode.single}
|
||||
selectionPreservedOnEmptyClick={true}
|
||||
/>
|
||||
<Paginator
|
||||
totalPageCount={data.venues.totalPageCount}
|
||||
currentPage={state.currentPageNumber}
|
||||
pageSize={state.pageSize}
|
||||
onPageChange={onPageChange}
|
||||
/>
|
||||
<Panel
|
||||
headerText='Venue'
|
||||
isOpen={state.isPanelOpen}
|
||||
onDismiss={onIsPanelOpen}
|
||||
>
|
||||
{state.selectedItems.length > 0 &&
|
||||
state.selectedItems.map((item: IVenue) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<p>{item.name}</p>
|
||||
<p>{item.id}</p>
|
||||
<p>{item.link}</p>
|
||||
<p>{item.active}</p>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</Panel>
|
||||
</React.Fragment>
|
||||
<Grid columns={1}>
|
||||
<Grid.Row>
|
||||
<Header as='h1'>
|
||||
Venues
|
||||
</Header>
|
||||
</Grid.Row>
|
||||
{appContext.state.notification?.isVisible && (
|
||||
<Grid.Row>
|
||||
<Grid.Column>
|
||||
<MessageBar
|
||||
messageBarType={appContext.state.notification?.messageBarType}
|
||||
isMultiline={appContext.state.notification?.isMultiline}
|
||||
onDismiss={onDismissMessageBar}
|
||||
>
|
||||
{appContext.state.notification?.message}
|
||||
</MessageBar>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
)}
|
||||
</div>
|
||||
<Grid.Row>
|
||||
<Grid.Column>
|
||||
{loading && (
|
||||
<Loader size='massive' active={loading}>Loading...</Loader>
|
||||
)}
|
||||
{!loading &&
|
||||
<Table selectable>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
{columns.map((column: string) => {
|
||||
return (
|
||||
<Table.HeaderCell>
|
||||
{column}
|
||||
</Table.HeaderCell>
|
||||
)
|
||||
})}
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{state.items.map((item) => {
|
||||
return (
|
||||
<Table.Row
|
||||
key={item.id}
|
||||
>
|
||||
<Table.Cell>
|
||||
{item.name}
|
||||
</Table.Cell>
|
||||
<Table.Cell textAlign='right'>
|
||||
<Button
|
||||
basic
|
||||
icon
|
||||
onClick={() => onDetails(item.id)}
|
||||
>
|
||||
<Icon name='info circle' />
|
||||
</Button>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)
|
||||
})}
|
||||
</Table.Body>
|
||||
<Table.Footer>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan='2'>
|
||||
<Grid columns={1}>
|
||||
<Grid.Row>
|
||||
<Grid.Column
|
||||
textAlign='right'
|
||||
>
|
||||
<Pagination
|
||||
totalPages={data?.venues?.totalPageCount}
|
||||
activePage={state.currentPageNumber}
|
||||
onPageChange={onPageChange}
|
||||
size='mini'
|
||||
/>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Footer>
|
||||
</Table>
|
||||
}
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +1,72 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_VENUES = gql`
|
||||
query GetVenues($pageNumber: Int, $pageSize: Int) {
|
||||
venues(pageNumber: $pageNumber, pageSize: $pageSize) {
|
||||
currentPage
|
||||
errors
|
||||
message
|
||||
export const GET_TEAMS = gql`
|
||||
query GetTeams($pageNumber: Int!, $pageSize: Int!) {
|
||||
teams(pageNumber: $pageNumber, pageSize: $pageSize) {
|
||||
pageSize
|
||||
succeeded
|
||||
totalItemCount
|
||||
message
|
||||
errors
|
||||
currentPage
|
||||
totalPageCount
|
||||
totalItemCount
|
||||
succeeded
|
||||
results {
|
||||
active
|
||||
id
|
||||
link
|
||||
name
|
||||
link
|
||||
id
|
||||
active
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_TEAM = gql`
|
||||
query GetTeam($id: Int!) {
|
||||
teamById(id: $id) {
|
||||
message
|
||||
errors
|
||||
succeeded
|
||||
results {
|
||||
name
|
||||
link
|
||||
id
|
||||
active
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const GET_VENUES = gql`
|
||||
query GetVenues($pageNumber: Int!, $pageSize: Int!) {
|
||||
venues(pageNumber: $pageNumber, pageSize: $pageSize) {
|
||||
pageSize
|
||||
message
|
||||
errors
|
||||
currentPage
|
||||
totalPageCount
|
||||
totalItemCount
|
||||
succeeded
|
||||
results {
|
||||
name
|
||||
link
|
||||
id
|
||||
active
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_VENUE_BY_ID = gql`
|
||||
query GetVenue($id: Int!) {
|
||||
venueById(id: $id) {
|
||||
message
|
||||
errors
|
||||
succeeded
|
||||
results {
|
||||
name
|
||||
link
|
||||
id
|
||||
active
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { INotification } from '../models/INotification';
|
||||
|
||||
export interface IAppContextState {
|
||||
notification: INotification | undefined;
|
||||
}
|
||||
7
app/src/context/app/IAppContextState.ts
Normal file
7
app/src/context/app/IAppContextState.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { ISidebar } from 'src/models/ISidebar';
|
||||
import { INotification } from '../../models/INotification';
|
||||
|
||||
export interface IAppContextState {
|
||||
notification: INotification | undefined;
|
||||
isSidebarVisible: boolean;
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { MessageBarType } from '@fluentui/react';
|
||||
import { INotification } from '../models/INotification';
|
||||
import { INotification } from '../../models/INotification';
|
||||
import { IAppContextState } from './IAppContextState';
|
||||
|
||||
export type Action = { type: 'SET_NOTIFICATION'; payload: INotification };
|
||||
export type Action =
|
||||
| { type: 'SET_NOTIFICATION'; payload: INotification }
|
||||
| { type: 'SET_IS_SIDEBAR_VISIBLE', payload: boolean };
|
||||
|
||||
export const initialState: IAppContextState = {
|
||||
notification: {
|
||||
@@ -10,7 +12,8 @@ export const initialState: IAppContextState = {
|
||||
message: 'Blah blah blah',
|
||||
isMultiline: false,
|
||||
isVisible: true
|
||||
}
|
||||
},
|
||||
isSidebarVisible: false
|
||||
};
|
||||
|
||||
export const reducer = (
|
||||
@@ -24,5 +27,14 @@ export const reducer = (
|
||||
notification: action.payload
|
||||
};
|
||||
}
|
||||
case 'SET_IS_SIDEBAR_VISIBLE': {
|
||||
return {
|
||||
...state,
|
||||
isSidebarVisible: action.payload
|
||||
}
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
||||
8
app/src/context/sidebar/ISidebarContextProps.ts
Normal file
8
app/src/context/sidebar/ISidebarContextProps.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import { ISidebarContextState } from './ISidebarContextState';
|
||||
import { Action } from './reducer';
|
||||
|
||||
export interface ISidebarContextProps {
|
||||
state: ISidebarContextState;
|
||||
dispatch: React.Dispatch<Action>;
|
||||
}
|
||||
5
app/src/context/sidebar/ISidebarContextProviderProps.ts
Normal file
5
app/src/context/sidebar/ISidebarContextProviderProps.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from "react";
|
||||
|
||||
export interface ISidebarContextProviderProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
8
app/src/context/sidebar/ISidebarContextState.ts
Normal file
8
app/src/context/sidebar/ISidebarContextState.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import { SidebarProps } from 'semantic-ui-react';
|
||||
|
||||
export interface ISidebarContextState {
|
||||
content: React.ReactNode;
|
||||
sidebar: SidebarProps;
|
||||
title: string;
|
||||
}
|
||||
4
app/src/context/sidebar/Sidebar.module.scss
Normal file
4
app/src/context/sidebar/Sidebar.module.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
.sidebar {
|
||||
background-color: #FFFFFF;
|
||||
padding: 30px;
|
||||
}
|
||||
4
app/src/context/sidebar/SidebarContext.tsx
Normal file
4
app/src/context/sidebar/SidebarContext.tsx
Normal file
@@ -0,0 +1,4 @@
|
||||
import { Context, createContext } from 'react';
|
||||
import { ISidebarContextProps } from './ISidebarContextProps';
|
||||
|
||||
export const SidebarContext: Context<ISidebarContextProps> = createContext<ISidebarContextProps>({} as ISidebarContextProps);
|
||||
53
app/src/context/sidebar/SidebarContextProvider.tsx
Normal file
53
app/src/context/sidebar/SidebarContextProvider.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React, { useEffect, useMemo, useReducer } from 'react';
|
||||
import styles from './Sidebar.module.scss';
|
||||
import { SidebarContext } from "./SidebarContext";
|
||||
import { ISidebarContextProps } from './ISidebarContextProps';
|
||||
import { ISidebarContextProviderProps } from './ISidebarContextProviderProps';
|
||||
import { initialState, reducer } from './reducer';
|
||||
import { Button, Grid, Icon, Sidebar } from 'semantic-ui-react';
|
||||
|
||||
const SidebarContextProvider: React.FC<ISidebarContextProviderProps> = (props: ISidebarContextProviderProps) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
const contextValue: ISidebarContextProps = useMemo(() => {
|
||||
return {
|
||||
state,
|
||||
dispatch
|
||||
}
|
||||
}, [state, dispatch]);
|
||||
|
||||
const onCloseSidebar = () => {
|
||||
dispatch({ type: 'SET_SIDEBAR_VISIBLE', payload: !state.sidebar?.visible });
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Sidebar
|
||||
className={styles.sidebar}
|
||||
{...state.sidebar}
|
||||
>
|
||||
<Grid columns={1}>
|
||||
<Grid.Row>
|
||||
<Grid.Column
|
||||
textAlign='right'
|
||||
>
|
||||
<Button>
|
||||
<Icon name='close' size='large' onClick={onCloseSidebar} />
|
||||
</Button>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
{state.content}
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
<Button onClick={onCloseSidebar}>Close</Button>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Sidebar>
|
||||
<SidebarContext.Provider value={contextValue}>
|
||||
{props.children}
|
||||
</SidebarContext.Provider>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default SidebarContextProvider;
|
||||
52
app/src/context/sidebar/reducer.ts
Normal file
52
app/src/context/sidebar/reducer.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import { SidebarProps } from "semantic-ui-react";
|
||||
import { ISidebarContextState } from "./ISidebarContextState";
|
||||
|
||||
export type Action =
|
||||
| { type: 'SET_SIDEBAR', payload: { sidebar: SidebarProps, content: React.ReactNode }}
|
||||
| { type: 'SET_SIDEBAR_PROPS', payload: SidebarProps}
|
||||
| { type: 'SET_SIDEBAR_CONTENT', payload: React.ReactNode }
|
||||
| { type: 'SET_SIDEBAR_VISIBLE', payload: boolean }
|
||||
|
||||
export const initialState: ISidebarContextState = {
|
||||
sidebar: {
|
||||
visible: false
|
||||
},
|
||||
content: undefined
|
||||
}
|
||||
|
||||
export const reducer = (state: ISidebarContextState, action: Action): ISidebarContextState => {
|
||||
switch (action.type) {
|
||||
case 'SET_SIDEBAR': {
|
||||
return {
|
||||
...state,
|
||||
sidebar: action.payload.sidebar,
|
||||
content: action.payload.content
|
||||
}
|
||||
}
|
||||
case 'SET_SIDEBAR_PROPS': {
|
||||
return {
|
||||
...state,
|
||||
sidebar: action.payload
|
||||
}
|
||||
}
|
||||
case 'SET_SIDEBAR_CONTENT': {
|
||||
return {
|
||||
...state,
|
||||
content: action.payload
|
||||
}
|
||||
}
|
||||
case 'SET_SIDEBAR_VISIBLE': {
|
||||
return {
|
||||
...state,
|
||||
sidebar: {
|
||||
...state.sidebar,
|
||||
visible: action.payload
|
||||
}
|
||||
}
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
import { AppContext } from '../../context/AppContext';
|
||||
import { AppContext } from '../../context/app/AppContext';
|
||||
|
||||
export const useAppContext = () => {
|
||||
const { state, dispatch } = useContext(AppContext);
|
||||
|
||||
11
app/src/hooks/sidebarContext/UseSidebarContext.tsx
Normal file
11
app/src/hooks/sidebarContext/UseSidebarContext.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { useContext } from 'react';
|
||||
import { SidebarContext } from '../../context/sidebar/SidebarContext';
|
||||
|
||||
export const useSidebarContext = () => {
|
||||
const { state, dispatch } = useContext(SidebarContext);
|
||||
|
||||
return {
|
||||
state,
|
||||
dispatch
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,13 @@ import React from 'react';
|
||||
import { createRoot, Root } from 'react-dom/client';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { ApolloClient, InMemoryCache, ApolloProvider, NormalizedCacheObject } from '@apollo/client';
|
||||
import AppContextProvider from './context/AppContextProvider';
|
||||
import AppContextProvider from './context/app/AppContextProvider';
|
||||
import App from './components/app/App';
|
||||
import 'semantic-ui-css/semantic.min.css';
|
||||
import SidebarContextProvider from './context/sidebar/SidebarContextProvider';
|
||||
|
||||
const client: ApolloClient<NormalizedCacheObject> = new ApolloClient({
|
||||
uri: 'http://localhost:8080/v1/graphql',
|
||||
uri: 'http://localhost:8081/graphql',
|
||||
cache: new InMemoryCache()
|
||||
});
|
||||
|
||||
@@ -14,12 +16,14 @@ const container = document.getElementById('app-root') as HTMLElement;
|
||||
const root: Root = createRoot(container);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<AppContextProvider>
|
||||
<ApolloProvider client={client}>
|
||||
<Router>
|
||||
<App />
|
||||
</Router>
|
||||
</ApolloProvider>
|
||||
</AppContextProvider>
|
||||
<ApolloProvider client={client}>
|
||||
<AppContextProvider>
|
||||
<SidebarContextProvider>
|
||||
<Router>
|
||||
<App />
|
||||
</Router>
|
||||
</SidebarContextProvider>
|
||||
</AppContextProvider>
|
||||
</ApolloProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
6
app/src/models/ISidebar.ts
Normal file
6
app/src/models/ISidebar.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
export interface ISidebar {
|
||||
component: React.ReactNode | undefined;
|
||||
isVisible: boolean;
|
||||
}
|
||||
23
app/src/models/ITeam.ts
Normal file
23
app/src/models/ITeam.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export interface ITeam {
|
||||
id: number;
|
||||
name: string;
|
||||
abbreviation: string;
|
||||
active: string;
|
||||
allStarStatus: string;
|
||||
clubName: string;
|
||||
divisionId: number;
|
||||
fileCode: string;
|
||||
firstYearOfPlay: number;
|
||||
franchiseName: string;
|
||||
leagueId: number;
|
||||
link: string;
|
||||
locationName: string;
|
||||
season: string;
|
||||
shortName: string;
|
||||
springLeagueId: number;
|
||||
sprintVenueId: number;
|
||||
sportId: number;
|
||||
teamCode: string;
|
||||
teamName: string;
|
||||
venueId: number;
|
||||
}
|
||||
Reference in New Issue
Block a user