by Hancheng Zhao
Roles
Admin
Advisor
Student
Sections
Announcements
Projects
Peer Review
Dashboard
Roles
Admin
Advisor
Student
Sections
Announcements
Projects
Peer Review
Dashboard
CRUD
View
View
Roles
Admin
Advisor
Student
Sections
Announcements
Projects
Peer Review
Dashboard
CRUD
CRUD own
Apply
Email Service
Roles
Admin
Advisor
Student
Sections
Announcements
Projects
Peer Review
Dashboard
Generate & Check
Generate & Check own
Review
Roles
Admin
Advisor
Student
Sections
Announcements
Projects
Peer Review
Dashboard
Roster/Application/Semester
Roster/Application
Team info
Structure
class App extends Component {
constructor () {
this.state = {
loading: true
}
}
...
render() {
return (
<div>
<Header />
{this.state.loading === true
? (<h2>Loading...</h2>)
: (
<div className="App">
<Route exact path="/" component={Home}/>
<Route path="/announcement" component={Announcement}/>
<Route path="/projects" component={Projects}/>
<Route path="/peer-review" component={PeerReview}/>
</div>
)}
<Footer />
</div>
);
}
}
export default App;Componet-Based
class App extends Component {
constructor () {
this.state = {
loading: true
}
}
...
render() {
return (
<div>
<Header />
{this.state.loading === true
? (<h2>Loading...</h2>)
: (
<div className="App">
<Route exact path="/" component={Home}/>
<Route path="/announcement" component={Announcement}/>
<Route path="/projects" component={Projects}/>
<Route path="/peer-review" component={PeerReview}/>
</div>
)}
<Footer />
</div>
);
}
}
export default App;class Footer extends Component {
render() {
return (
<div className="container">
<div className="footer">
<div id="innovation-footer">
<div id="innovation-bar">
<div className="innovation-top">
<div className="innovation-status">
<a href="https://yourfuture.asu.edu/rankings"><span>ASU is #1 in the U.S. for Innovation</span></a>
</div>
<div className="innovation-hidden">
<a href="https://yourfuture.asu.edu/rankings"><img src="//www.asu.edu/asuthemes/4.6/assets/best-college-2017.png" alt="Best Colleges U.S. News Most Innovative 2017"/></a>
</div>
</div>
</div>
<div className="footer-menu">
<ul className="default">
<li className="links-footer"><a href="http://www.asu.edu/copyright/">Copyright & Trademark</a></li>
<li className="links-footer"><a href="http://www.asu.edu/accessibility/">Accessibility</a></li>
<li className="links-footer"><a href="http://www.asu.edu/privacy/">Privacy</a></li>
<li className="links-footer"><a href="http://www.asu.edu/asujobs">Jobs</a></li>
<li className="links-footer"><a href="http://www.asu.edu/emergency/">Emergency</a></li>
<li className="no-border links-footer"><a href="http://www.asu.edu/contactasu/">Contact ASU</a></li>
</ul>
</div>
</div>
</div>
</div>
);
}
}Componet-Based
Componet-Based
QuestionCard
Componet-Based
QuestionContainer
Componet-Based
QuestionContainer
{questions.map((question, i) => (
<QuestionCard
key={question.id}
index={i}
id={question.id}
type={question.type}
data={question.data}
moveQuestion={this.moveQuestion}
removeQuestion={this.removeQuestion}
updateQuestion={this.updateQuestion}
/>
))}Componet-Based
QuestionContainer
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js --env=jsdom",
"deploy": "cp -r ./build/. ../firebase/public && cd ../firebase/ && firebase deploy --only hosting"
}1. Define your state and make it observable
2. Create a view that responds to changes in the State
3. Modify the State
state container
decorator
import { observable } from "mobx";
class UserStore {
@observable authed = false;
login() {
this.authed = true;
}
logout() {
this.authed = false;
}
}
const userStore = new UserStore();
export default userStore;import { observer } from "mobx-react";
@observer
class App extends Component {
constructor () {
...
}
componentDidMount () {
this.userStateChange = firebase.auth().onAuthStateChanged((user) => {
if (user) {
userStore.login()
...
}).catch((noRole) => { //unable to retrieve role from db
userStore.fetchUserRole(noRole);
})
} else {
userStore.logout()
}
})
}
...
}export const AdminRoute = ({component: Component, user, ...rest}) => (
<Route {...rest}
render={(props) => user.role === "admin"
? <Component {...props} />
: <Redirect to='/login'/>}
/>
)
export const PrivateRoute = ({component: Component, authed, ...rest}) => (
<Route {...rest}
render={(props) => authed === true
? <Component {...props} />
: <Redirect to={{pathname: '/login', state: {from: props.location}}} />}
/>
)
const Announcement = ( {match} ) => (
<div>
<Switch>
<AdminRoute exact path={`${match.url}/creation`} user={userStore} component={ AnnouncementCreate }/>
<Route exact path={`${match.url}/edit/:announcementId`} user={userStore} component={ AnnouncementEdit }/>
<Route path={`${match.url}/:announcementId`} component={ AnnouncementPage }/>
<Route exact path={match.url} component={ AnnouncementList }/>
</Switch>
</div>
)
export default Announcement;
googleLogin(user) {
let provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithRedirect(provider);
firebase.auth().getRedirectResult(provider).then((result) => {
//...
}).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log("errorMessage: " + errorMessage);
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
})
}const currentTime = new Date().getTime();
let deleteOverDue = admin.database().ref().child(Announcement_Path)
.orderByChild('endDate')
.endAt(currentTime)
.once("value")
.then(snap => {
sunset = snap.val();
console.log(sunset);
if (sunset) {
return admin.database().ref()
.child(Announcement_Sunset)
.update(sunset);
}
})const ref = firebase.storage().ref()
let photoRef = ref.child("announcement_photos/" + newdt + "/" + photo.name);
photoRef.put(photo).then((snap) => {
this.setState(prevState => ({
content : prevState.content + `\n\n<img alt="${photo.name}" src="${snap.downloadURL}" width="50%">`
}))
})
Cloud Firestore Triggers
Realtime Database Triggers
Firebase Authentication Triggers
Google Analytics for Firebase Triggers
Crashlytics Triggers
Cloud Storage Triggers
Cloud Pub/Sub Triggers
HTTP Triggers
Cron-job
functions.https.onRequest((req, res) => {
const key = req.query.key;
// Exit if the keys don't match
if (!secureCompare(key, functions.config().cron.key)) {
console.log('The key provided in the request does not match the key set in the environment. Check that', key,
'matches the cron.key attribute in `firebase env:get`');
res.status(403).send('Security key does not match. Make sure your "key" URL query parameter matches the ' +
'cron.key environment variable.');
return;
}
const currentTime = new Date().getTime();
let newAnnouncement;
let sunset;
//check startDate in raw data
let addWaitedList = admin.database().ref().child(Announcement_Raw_Data).orderByChild('startDate').endAt(currentTime).once("value")
.then(snap => {
newAnnouncement = snap.val();
console.log(newAnnouncement);
if (newAnnouncement) {
return admin.database().ref().child(Announcement_Path).update(newAnnouncement)
}
}).then(() => {
let update = {}
Object.keys(newAnnouncement).forEach((uuid) => {
update[uuid] = null;
});
console.log(Object.keys(newAnnouncement).length + " announcements have been updated");
admin.database().ref(Announcement_Raw_Data).update(update);
}).catch(err => {
res.send(err)
})
//check endDate in announcements
let deleteOverDue = admin.database().ref().child(Announcement_Path).orderByChild('endDate').endAt(currentTime).once("value")
.then(snap => {
sunset = snap.val()
console.log(sunset)
if (sunset) {
return admin.database().ref().child(Announcement_Sunset).update(sunset)
}
}).then(() => {
if (sunset) {
let update = {}
Object.keys(sunset).forEach((uuid) => {
update[uuid] = null;
});
console.log(Object.keys(sunset).length + " announcements have been sunset");
return admin.database().ref().child(Announcement_Path).update(update);
} else {
console.log("No announcements need to be sunset")
}
}).catch(err => {
res.send(err)
})
Promise.all([addWaitedList, deleteOverDue]).then(value => {
console.log(`Cron job for ${new Date()} has been completed`)
});
})Hello -name-,
A new team application was submitted, please go to the dashboard to checkout.
<%body%>
VIP Email Service
Template
Object.keys(adminLists).forEach((uuid) => {
let request = sg.emptyRequest({
method: 'POST',
path: '/v3/mail/send',
body: {
personalizations: [{
to: [{ email: adminLists[uuid].email }],
'substitutions': {
'-name-': adminLists[uuid].name,
},
subject: 'A new team application is submitted'
}],
from: {
email: 'noreply@vip.udel.edu'
},
content: [{
type: 'text/html',
value: `<p>Applicaiton information:</p> ${formatted.join("")}`
}],
'template_id': functions.config().sendgrid.templateid,
}
});
// With promise
sg.API(request)
.then(function(response) {
console.log(response.statusCode);
console.log(response.body);
console.log(response.headers);
})
.catch(function(error) {
// error is an instance of SendGridError
// The full response is attached to error.response
console.log(error.response.statusCode);
});
});API