This commit is contained in:
Noah Spannbauer
2023-02-15 12:46:50 -06:00
parent c2d8bd1838
commit 0d5eb3e801
99 changed files with 20124 additions and 213 deletions

View File

@@ -0,0 +1,3 @@
.content {
padding: 30px;
}

View File

@@ -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>
);
};

View File

@@ -0,0 +1,4 @@
.siteFooter {
background-color: #e0e1e2;
height: 150px;
}

View 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;

View File

@@ -0,0 +1,4 @@
.siteHeader {
background-color: #e0e1e2;
height: 100px;
}

View 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;

View File

@@ -1,3 +0,0 @@
export interface IMenuProps {
selectedKey: string;
}

View File

@@ -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;

View File

@@ -0,0 +1,3 @@
export interface INavigationState {
activeLink: string;
}

View 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;

View 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
}
}
}
}

View 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;
}

View 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;

View 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
};
}
}
}

View File

@@ -0,0 +1,3 @@
export interface IVenueDetailsProps {
venueId: number;
}

View 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;

View File

@@ -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';

View File

@@ -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>
);
};

View File

@@ -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
}
}
}

View File

@@ -1,5 +0,0 @@
import { INotification } from '../models/INotification';
export interface IAppContextState {
notification: INotification | undefined;
}

View File

@@ -0,0 +1,7 @@
import { ISidebar } from 'src/models/ISidebar';
import { INotification } from '../../models/INotification';
export interface IAppContextState {
notification: INotification | undefined;
isSidebarVisible: boolean;
}

View File

@@ -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;
}
}
};

View 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>;
}

View File

@@ -0,0 +1,5 @@
import React from "react";
export interface ISidebarContextProviderProps {
children: React.ReactNode;
}

View 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;
}

View File

@@ -0,0 +1,4 @@
.sidebar {
background-color: #FFFFFF;
padding: 30px;
}

View File

@@ -0,0 +1,4 @@
import { Context, createContext } from 'react';
import { ISidebarContextProps } from './ISidebarContextProps';
export const SidebarContext: Context<ISidebarContextProps> = createContext<ISidebarContextProps>({} as ISidebarContextProps);

View 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;

View 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;
}
}
}

View File

@@ -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);

View 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
}
}

View File

@@ -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>
);

View 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
View 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;
}