Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Khalid Ali
schedules
Commits
f3699b03
Commit
f3699b03
authored
Jun 11, 2018
by
Andrew Hrdy
Browse files
Added redux
parent
7d3267e4
Changes
14
Hide whitespace changes
Inline
Side-by-side
schedules_web/package.json
View file @
f3699b03
...
...
@@ -22,6 +22,7 @@
"dependencies"
:
{
"@types/react"
:
"^16.3.12"
,
"@types/react-dom"
:
"^16.0.5"
,
"@types/react-redux"
:
"^6.0.2"
,
"babel-loader"
:
"^7.1.4"
,
"clean-webpack-plugin"
:
"^0.1.19"
,
"css-loader"
:
"^0.28.11"
,
...
...
@@ -31,6 +32,9 @@
"masonstrap"
:
"https://git.gmu.edu/srct/masonstrap.git"
,
"react"
:
"^16.3.2"
,
"react-dom"
:
"^16.3.2"
,
"react-redux"
:
"^5.0.7"
,
"redux"
:
"^4.0.0"
,
"redux-thunk"
:
"^2.3.0"
,
"style-loader"
:
"^0.21.0"
,
"url-loader"
:
"^1.0.1"
},
...
...
schedules_web/src/actions/schedule/schedule.action-types.ts
0 → 100644
View file @
f3699b03
export
const
ADD_SECTION
=
'
[Schedule] ADD_SECTION
'
;
export
const
REMOVE_SECTION
=
'
[Schedule] REMOVE_SECTION
'
;
\ No newline at end of file
schedules_web/src/actions/schedule/schedule.actions.ts
0 → 100644
View file @
f3699b03
import
{
ADD_SECTION
,
REMOVE_SECTION
}
from
"
./schedule.action-types
"
;
import
{
Section
}
from
"
../../ts/section
"
;
export
interface
ScheduleAction
{
type
:
string
;
section
:
Section
;
}
export
const
addSection
=
(
section
:
Section
):
ScheduleAction
=>
{
return
{
type
:
ADD_SECTION
,
section
:
section
};
}
export
const
removeSection
=
(
section
:
Section
):
ScheduleAction
=>
{
return
{
type
:
REMOVE_SECTION
,
section
:
section
};
}
\ No newline at end of file
schedules_web/src/actions/search/search.action-types.ts
0 → 100644
View file @
f3699b03
export
const
SET_SEARCH_SECTIONS
=
'
[Search] SET_SECTIONS
'
;
\ No newline at end of file
schedules_web/src/actions/search/search.actions.ts
0 → 100644
View file @
f3699b03
import
{
SET_SEARCH_SECTIONS
}
from
'
./search.action-types
'
;
import
{
Section
}
from
'
../../ts/section
'
;
export
interface
SearchAction
{
type
:
string
;
sections
:
Section
[];
}
export
const
searchSections
=
(
crn
:
string
)
=>
async
(
dispatch
:
any
)
=>
{
const
response
=
await
fetch
(
`http://localhost:3000/api/search?crn=
${
crn
}
`
);
const
object
=
await
response
.
json
();
const
section
:
Section
=
{
id
:
object
.
id
,
name
:
object
.
name
,
title
:
object
.
title
,
crn
:
object
.
crn
,
instructor
:
object
.
instructor
,
location
:
object
.
location
,
days
:
object
.
days
,
startTime
:
object
.
start_time
,
endTime
:
object
.
end_time
,
};
dispatch
({
type
:
SET_SEARCH_SECTIONS
,
sections
:
[
section
]
});
}
\ No newline at end of file
schedules_web/src/components/Search.tsx
deleted
100644 → 0
View file @
7d3267e4
import
*
as
React
from
'
react
'
;
import
{
fetchSectionWithCRN
,
Section
}
from
'
../ts/section
'
;
import
SearchBar
from
'
./SearchBar
'
;
import
SectionList
from
'
./SectionList
'
;
interface
Props
{
addSearchResultCallback
?:
(
section
:
Section
)
=>
void
;
}
interface
State
{
sections
:
Section
[];
}
export
default
class
Search
extends
React
.
Component
<
Props
,
State
>
{
constructor
(
props
:
Props
)
{
super
(
props
);
this
.
state
=
{
sections
:
[]
};
}
render
()
{
return
(
<
div
>
<
SearchBar
onSearch
=
{
this
.
searchForSections
}
/>
<
SectionList
sections
=
{
this
.
state
.
sections
}
buttonText
=
"Add to schedule"
selectSectionCallback
=
{
this
.
props
.
addSearchResultCallback
}
/>
</
div
>
);
}
searchForSections
=
(
crn
:
string
)
=>
{
fetchSectionWithCRN
(
crn
).
then
(
section
=>
this
.
setState
({
sections
:
[
section
]
}));
};
}
schedules_web/src/co
mponent
s/App.tsx
→
schedules_web/src/co
ntainer
s/App.tsx
View file @
f3699b03
...
...
@@ -2,16 +2,19 @@ import * as React from 'react';
import
{
Section
}
from
'
../ts/section
'
;
import
{
downloadCalendar
,
ENDPOINTS
,
postData
}
from
'
../ts/utilities
'
;
import
Search
from
'
./Search
'
;
import
SectionList
from
'
./SectionList
'
;
interface
State
{
currentSchedule
:
Section
[];
import
SectionList
from
'
../components/SectionList
'
;
import
{
connect
}
from
'
react-redux
'
;
import
{
State
}
from
'
../reducers
'
;
import
{
removeSection
}
from
'
../actions/schedule/schedule.actions
'
;
interface
AppProps
{
schedule
:
Section
[];
removeSection
:
(
section
:
Section
)
=>
any
;
}
export
default
class
App
extends
React
.
Component
<
any
,
State
>
{
constructor
(
props
:
any
)
{
class
App
extends
React
.
Component
<
AppProps
>
{
constructor
(
props
:
AppProps
)
{
super
(
props
);
this
.
state
=
{
currentSchedule
:
[]
};
}
render
()
{
...
...
@@ -19,38 +22,32 @@ export default class App extends React.Component<any, State> {
<
div
>
<
h1
>
Schedules
</
h1
>
<
h2
>
Search
</
h2
>
<
Search
addSearchResultCallback
=
{
this
.
addSectionToCurrentScheduleIfUnique
}
/>
<
Search
/>
<
h2
>
Your schedule
</
h2
>
<
SectionList
sections
=
{
this
.
state
.
currentS
chedule
}
sections
=
{
this
.
props
.
s
chedule
}
buttonText
=
"Remove from schedule"
selectSectionCallback
=
{
this
.
removeFromSchedule
}
selectSectionCallback
=
{
this
.
props
.
removeSection
}
/>
<
button
onClick
=
{
this
.
generateSchedule
}
>
Generate Schedule
</
button
>
</
div
>
);
}
addSectionToCurrentScheduleIfUnique
=
(
section
:
Section
)
=>
{
if
(
!
this
.
isSectionInSchedule
(
section
))
{
this
.
setState
({
currentSchedule
:
[...
this
.
state
.
currentSchedule
,
section
]
});
}
};
isSectionInSchedule
=
(
section
:
Section
)
=>
this
.
state
.
currentSchedule
.
find
(
sectionInSchedule
=>
section
==
sectionInSchedule
);
// TODO: Only view logic should be in the component.
generateSchedule
=
()
=>
{
const
crns
=
this
.
state
.
currentS
chedule
.
map
(
section
=>
section
.
crn
);
const
crns
=
this
.
props
.
s
chedule
.
map
(
section
=>
section
.
crn
);
postData
(
ENDPOINTS
.
generateCalendar
,
crns
)
.
then
(
response
=>
response
.
text
())
.
then
(
icalText
=>
downloadCalendar
(
icalText
));
};
removeFromSchedule
=
(
section
:
Section
)
=>
{
this
.
setState
({
currentSchedule
:
this
.
state
.
currentSchedule
.
filter
(
other
=>
section
!==
other
),
});
};
}
const
mapStateToProps
=
(
state
:
State
)
=>
({
schedule
:
state
.
schedule
});
export
default
connect
(
mapStateToProps
,
{
removeSection
})(
App
);
\ No newline at end of file
schedules_web/src/containers/Search.tsx
0 → 100644
View file @
f3699b03
import
*
as
React
from
'
react
'
;
import
{
Section
}
from
'
../ts/section
'
;
import
SearchBar
from
'
../components/SearchBar
'
;
import
SectionList
from
'
../components/SectionList
'
;
import
{
connect
}
from
'
react-redux
'
;
import
{
State
}
from
'
../reducers
'
;
import
{
searchSections
}
from
'
../actions/search/search.actions
'
;
import
{
addSection
}
from
'
../actions/schedule/schedule.actions
'
;
interface
SearchProps
{
searchedSections
:
Section
[];
searchSections
:
(
crn
:
string
)
=>
any
;
addSection
:
(
section
:
Section
)
=>
any
;
}
class
Search
extends
React
.
Component
<
SearchProps
>
{
constructor
(
props
:
SearchProps
)
{
super
(
props
);
}
render
()
{
return
(
<
div
>
<
SearchBar
onSearch
=
{
this
.
props
.
searchSections
}
/>
<
SectionList
sections
=
{
this
.
props
.
searchedSections
}
buttonText
=
"Add to schedule"
selectSectionCallback
=
{
this
.
props
.
addSection
}
/>
</
div
>
);
}
}
const
mapStateToProps
=
(
state
:
State
)
=>
({
searchedSections
:
state
.
search
.
searchedSections
});
export
default
connect
(
mapStateToProps
,
{
searchSections
,
addSection
})(
Search
);
\ No newline at end of file
schedules_web/src/index.tsx
View file @
f3699b03
import
'
masonstrap/build/css/masonstrap.min.css
'
;
import
'
masonstrap/build/js/masonstrap.min.js
'
;
import
{
applyMiddleware
,
compose
,
createStore
}
from
'
redux
'
;
import
ReduxThunk
from
'
redux-thunk
'
;
import
{
Provider
}
from
'
react-redux
'
;
import
*
as
React
from
'
react
'
;
import
*
as
ReactDOM
from
'
react-dom
'
;
import
App
from
'
./components/App
'
;
import
App
from
'
./containers/App
'
;
import
{
reducers
}
from
'
./reducers
'
;
ReactDOM
.
render
(<
App
/>,
document
.
getElementById
(
'
root
'
));
declare
global
{
interface
Window
{
__REDUX_DEVTOOLS_EXTENSION__
?:
()
=>
any
;
}
}
const
extension
=
window
.
__REDUX_DEVTOOLS_EXTENSION__
&&
window
.
__REDUX_DEVTOOLS_EXTENSION__
();
const
isProduction
=
process
.
env
.
NODE_ENV
===
'
production
'
;
let
enhance
;
if
(
isProduction
||
!
extension
)
{
enhance
=
compose
(
applyMiddleware
(
ReduxThunk
));
}
else
{
enhance
=
compose
(
applyMiddleware
(
ReduxThunk
),
extension
);
}
const
store
=
createStore
(
reducers
,
enhance
);
ReactDOM
.
render
(
<
Provider
store
=
{
store
}
>
<
App
/>
</
Provider
>,
document
.
getElementById
(
'
root
'
));
\ No newline at end of file
schedules_web/src/reducers/index.ts
0 → 100644
View file @
f3699b03
import
{
schedule
,
ScheduleState
}
from
'
./schedule.reducer
'
;
import
{
search
,
SearchState
}
from
'
./search.reducer
'
;
export
interface
State
{
schedule
:
ScheduleState
search
:
SearchState
}
const
defaultState
:
State
=
{
schedule
:
[],
search
:
{
searchedSections
:
[]
}
};
export
const
reducers
=
(
state
:
State
=
defaultState
,
action
:
any
)
=>
({
schedule
:
schedule
(
state
.
schedule
,
action
),
search
:
search
(
state
.
search
,
action
)
})
\ No newline at end of file
schedules_web/src/reducers/schedule.reducer.ts
0 → 100644
View file @
f3699b03
import
{
Section
}
from
'
../ts/section
'
;
import
{
ADD_SECTION
,
REMOVE_SECTION
}
from
'
../actions/schedule/schedule.action-types
'
;
import
{
ScheduleAction
}
from
'
../actions/schedule/schedule.actions
'
;
export
type
ScheduleState
=
Section
[];
export
const
schedule
=
(
state
:
ScheduleState
=
[],
action
:
ScheduleAction
)
=>
{
switch
(
action
.
type
)
{
case
ADD_SECTION
:
return
state
.
findIndex
(
s
=>
s
.
crn
===
action
.
section
.
crn
)
===
-
1
?
[...
state
,
action
.
section
]
:
state
;
case
REMOVE_SECTION
:
return
state
.
filter
(
s
=>
s
.
crn
!==
action
.
section
.
crn
);
default
:
return
state
;
}
};
\ No newline at end of file
schedules_web/src/reducers/search.reducer.ts
0 → 100644
View file @
f3699b03
import
{
SearchAction
}
from
'
./../actions/search/search.actions
'
;
import
{
Section
}
from
'
./../ts/section
'
;
import
{
SET_SEARCH_SECTIONS
}
from
'
../actions/search/search.action-types
'
;
export
interface
SearchState
{
searchedSections
:
Section
[];
}
export
const
search
=
(
state
:
SearchState
=
{
searchedSections
:
[]},
action
:
SearchAction
)
=>
{
switch
(
action
.
type
)
{
case
SET_SEARCH_SECTIONS
:
return
Object
.
assign
({},
state
,
{
searchedSections
:
[...
action
.
sections
]
});
default
:
return
state
;
}
}
\ No newline at end of file
schedules_web/src/ts/section.ts
View file @
f3699b03
...
...
@@ -9,20 +9,3 @@ export interface Section {
startTime
:
string
;
endTime
:
string
;
}
export
async
function
fetchSectionWithCRN
(
crn
:
string
):
Promise
<
Section
>
{
const
response
=
await
fetch
(
`http://localhost:3000/api/search?crn=
${
crn
}
`
);
const
object
=
await
response
.
json
();
return
{
id
:
object
.
id
,
name
:
object
.
name
,
title
:
object
.
title
,
crn
:
object
.
crn
,
instructor
:
object
.
instructor
,
location
:
object
.
location
,
days
:
object
.
days
,
startTime
:
object
.
start_time
,
endTime
:
object
.
end_time
,
};
}
schedules_web/yarn.lock
View file @
f3699b03
...
...
@@ -91,6 +91,13 @@
"@types/node" "*"
"@types/react" "*"
"@types/react-redux@^6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-6.0.2.tgz#10069b53db8e0920fd8656e068dcf10c53c9ad2a"
dependencies:
"@types/react" "*"
redux "^4.0.0"
"@types/react@*", "@types/react@^16.3.12":
version "16.3.17"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.17.tgz#d59d1a632570b0713946ed9c2949d994773633c5"
...
...
@@ -3968,6 +3975,10 @@ hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
hoist-non-react-statics@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40"
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
...
...
@@ -4273,7 +4284,7 @@ into-stream@^3.1.0:
from2 "^2.1.1"
p-is-promise "^1.1.0"
invariant@^2.2.2:
invariant@^2.0.0,
invariant@^2.2.2:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
dependencies:
...
...
@@ -4977,6 +4988,10 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
lodash-es@^4.17.5:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.10.tgz#62cd7104cdf5dd87f235a837f0ede0e8e5117e05"
lodash._basecopy@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
...
...
@@ -6828,6 +6843,17 @@ react-dom@^16.3.2:
object-assign "^4.1.1"
prop-types "^15.6.0"
react-redux@^5.0.7:
version "5.0.7"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8"
dependencies:
hoist-non-react-statics "^2.5.0"
invariant "^2.0.0"
lodash "^4.17.5"
lodash-es "^4.17.5"
loose-envify "^1.1.0"
prop-types "^15.6.0"
react@^16.3.2:
version "16.4.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585"
...
...
@@ -6966,6 +6992,17 @@ reduce-function-call@^1.0.1:
dependencies:
balanced-match "^0.4.2"
redux-thunk@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
redux@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.0.tgz#aa698a92b729315d22b34a0553d7e6533555cc03"
dependencies:
loose-envify "^1.1.0"
symbol-observable "^1.2.0"
regenerate@^1.2.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
...
...
@@ -7927,7 +7964,7 @@ symbol-observable@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
symbol-observable@^1.1.0:
symbol-observable@^1.1.0
, symbol-observable@^1.2.0
:
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment