update,
This commit is contained in:
213
assignment/notes.md
Normal file
213
assignment/notes.md
Normal file
@@ -0,0 +1,213 @@
|
||||
|
||||
In this assignment, we are going to develop a simple single-page iNotes application using the
|
||||
MERN stack (MongoDB, Express.JS, ReactJS, and Node.js). The main workflow of the iNotes
|
||||
application is as follows.
|
||||
Ø Upon loading, the sketch of the page is shown in Fig. 1:
|
||||
Fig. 1
|
||||
Ø After a user (e.g., Andy) has logged in, the sketch of the page is in Fig. 2. The user’s
|
||||
icon, user name and a logout button are displayed on the top. A list of this user’s
|
||||
notes are shown on the left panel (node title only) together with a search bar, and
|
||||
the right panel is empty except an icon indicating the creation of a new note (i.e., the
|
||||
“New note icon”). The node titles in the left panel should be listed in reverse
|
||||
chronological order of the last saved time of the notes. The total number of notes
|
||||
should be given in the ( ) on the top of the list.
|
||||
Fig. 2
|
||||
Ø After one clicks on the “New note icon” (on any page view where it is shown), a new
|
||||
node creation panel shows in the right panel (Fig. 3). There is a node title input field
|
||||
and a note content input field, into which the user can enter texts. There is a
|
||||
“Cancel” button, clicking which a confirmation box “Are you sure to quit editing the
|
||||
note?” will be popped up: if the user confirms quitting, the page view goes back to
|
||||
the one shown in Fig. 2; otherwise, the current page view remains. There is a “Save”
|
||||
button, clicking which the newly created note is shown on the right panel, with the
|
||||
“Last saved” time and a “Delete” button displayed on top of the note, as shown in
|
||||
Fig. 4; besides, the note title should be listed in the left panel, as the first in the list
|
||||
(as it is the latest), the note title should be highlighted in a different color than the
|
||||
rest of the note titles in the list (since this note’s content is shown in the right panel),
|
||||
and the total number of notes in ( ) on top of the list should be incremented.
|
||||
Fig. 3 Fig. 4
|
||||
Ø At any time, when one clicks on one note title in the left panel, the note’s content
|
||||
should be displayed in the right panel, as shown in Fig. 5 (which is in fact the same
|
||||
page view as Fig. 4), and the node title in the left panel should be highlighted. On the
|
||||
page view (i.e., Fig. 4 or Fig. 5’s page view), if one clicks into the note title input field
|
||||
or note content input field, the page view changes to the one in Fig. 6, with a
|
||||
“Cancel” button and a “Save” button (indicating a note editing mode). When
|
||||
“Cancel” is clicked, a confirmation box “Are you sure to quit editing the note?” will
|
||||
be popped up: if the user confirms quitting, the page goes back to the previous note
|
||||
view (Fig. 4 or Fig. 5); otherwise, the current page view remains. When “Save” is
|
||||
clicked, a page view as in Fig. 4 or Fig. 5 is shown, except that the “Last saved” time
|
||||
on the top of the right panel should be the updated latest note saved time.
|
||||
Fig. 5 Fig. 6
|
||||
Ø One can input a search string into the search bar at the top of the left panel and
|
||||
press “Enter” on the keyboard. Then only the notes whose title or content contains
|
||||
the search string will be listed in the left panel, ordered in reserve chronological
|
||||
order of their last saved time, and the number in ( ) shows the number of matched
|
||||
notes. On a page view as in Fig. 3, 4, 5 or 6, the search does not influence the display
|
||||
in the right panel, and if the note whose details are displayed in the right panel
|
||||
matches the search, its title in the searched list in the left panel should be
|
||||
highlighted. For a search on a page view as in Fig. 6, last saved title and content of
|
||||
the note that is being edited are used for matching the search; when the note is
|
||||
saved, if its title and content do not match the search, it will not be displayed in the
|
||||
searched list in the left panel.
|
||||
Ø On a page view as in Fig. 4 or Fig. 5, after one clicks the “Delete” button, a
|
||||
confirmation box pops up showing “Confirm to delete this note?” If the user
|
||||
confirms the deletion, the note information will be removed from both the left and
|
||||
right panels. In the left panel, the total note number will be decremented; the right
|
||||
panel will show no note information as in Fig. 2. If the user cancels the deletion, the
|
||||
page view remains unchanged.
|
||||
Ø When one clicks the “log out” button on a page view as in Fig. 2, 4 or 5, the page
|
||||
view directly goes back to Fig. 1. When one clicks the “log out” button on a page
|
||||
view as in Fig. 3 or 6, an alert box “Are you sure to quit editing the note and log
|
||||
out?” will be popped up: if the user confirms quitting, the page view goes back to Fig.
|
||||
1; otherwise, the current page view remains.
|
||||
We are going to achieve this web application by implementing code in a backend Express app
|
||||
and a frontend React app.
|
||||
• Express app:
|
||||
app.js
|
||||
./routes/notes.js
|
||||
• React app:
|
||||
./src/App.js
|
||||
./src/index.js
|
||||
./src/App.css
|
||||
Task 1. Backend Web Service
|
||||
We implement the backend web service logic using Express.js. The web service is accessed at
|
||||
http://localhost:3001/xx.
|
||||
Preparations
|
||||
1. Following steps in setup_nodejs_runtime_and_examples_1.pdf, create an Express project
|
||||
named NoteService.
|
||||
2. Following steps in AJAX_JSON_MongoDB_setup_and_examples.pdf, run MongoDB server,
|
||||
and create a database “assignment2” in the database server.
|
||||
3. Insert a few user records to a userList collection in the database in the format as follows.
|
||||
We will assume that user names are all different in this application.
|
||||
db.userList.insert({'name': 'Andy', 'password': '123456', 'icon': 'icons/andy.jpg'})
|
||||
Create a folder “icons” under the public folder in your Express project directory
|
||||
(NoteService). Copy a few icon images to the icons folder. For implementation simplicity, we
|
||||
do not store icon images in MongoDB. Instead, we store them in the harddisk under the
|
||||
NoteService/public/icons/ folder, and store the path of an icon in the userList collection only,
|
||||
using which we can identify the icon image in the icons folder.
|
||||
Insert a number of records to a noteList collection in the database in the format as follows,
|
||||
each corresponding to one note in the app. Here userId should be the value of _id of the
|
||||
record in the userList collection, corresponding to the user who added the note.
|
||||
db.noteList.insert({'userId': 'xxx', 'lastsavedtime': '20:12:10 Tue Nov 15 2022', 'title':
|
||||
'assigment2', 'content': 'an iNotes app based on react'})
|
||||
Implement backend web service logic (NoteService/app.js,
|
||||
NoteService/routes/notes.js)
|
||||
app.js (10 marks)
|
||||
In app.js, load the router module implemented in ./routes/notes.js. Then add the
|
||||
middleware to specify that all requests for http://localhost:3001/ should be handled by this
|
||||
router.
|
||||
Add necessary code for loading the MongoDB database you have created, creating an
|
||||
instance of the database, and passing the database instance for usage of all middlewares.
|
||||
Also load any other modules and add other middlewares which you may need to implement
|
||||
this application.
|
||||
We will let the server run on the port 3001 and launch the Express app using command
|
||||
“node app.js”.
|
||||
./routes/notes.js (22 marks)
|
||||
In notes.js, implement the router module with middlewares to handle the following
|
||||
endpoints:
|
||||
1. HTTP POST requests for http://localhost:3001/signin. The middleware should parse the
|
||||
body of the HTTP POST request and extract the username and password carried in request
|
||||
body. Then it checks whether the username and password match any record in the userList
|
||||
collection in the database. If no, send “Login failure” in the body of the response message. If
|
||||
yes, create a session variable “userId” and store this user’s _id in the session variable.
|
||||
Retrieve name and icon of the current user (according to the value of the “userId” session
|
||||
variable), _id, lastsavedtime and title of all notes of the current user from the respective
|
||||
collections in the MongoDB database. Send all retrieved information as a JSON string to the
|
||||
client if database operations are successful, and the error if failure. You should decide the
|
||||
format of the JSON string and parse it accordingly in the front-end code to be implemented
|
||||
in Task 2.
|
||||
2. HTTP GET requests for http://localhost:3001/logout. The middleware should clear the
|
||||
“userId” session variable and send an empty string back to the user.
|
||||
3. HTTP GET requests for http://localhost:3001/getnote?noteid=xx. Retrieve _id,
|
||||
lastsavedtime, title and content of the note from the noteList collection based on the value
|
||||
of “nodeid” carried in the URL. Send retrieved information as a JSON string in the body of the
|
||||
response message if database operations are successful, and the error if failure. You should
|
||||
decide the format of the JSON string to be included in the response body.
|
||||
4. HTTP POST requests for http://localhost:3001/addnote. The middleware should parse the
|
||||
body of the HTTP POST request and extract the note title and content carried in the request
|
||||
body. Then it saves the new note into the noteList collection together with the _id of the
|
||||
current user (based on the value of “userId” session variable) and the current time on the
|
||||
server as the lastsavedtime. Return the lastsavedtime and _id of the note document in the
|
||||
nodeList collection to the client in JSON if database operations are successful, and the error
|
||||
if failure.
|
||||
5. HTTP PUT requests for http://localhost:3001/savenote/:noteid. The middleware should
|
||||
update the lastsavedtime, title and content of the note in the noteList collection based on
|
||||
the nodeid carried in the URL, the current time on the server and the data contained in the
|
||||
body of the request message. Return the lastsavedtime to the client if success and the error
|
||||
if failure.
|
||||
6. HTTP GET requests for http://localhost:3001/searchnotes?searchstr=xx. The middleware
|
||||
should find in the noteList collection all notes of the current user (based on the value of
|
||||
“userId” session variable) whose title or content contains the searchstr carried in the URL.
|
||||
Send _id, lastsavedtime and title of those notes in JSON to the client if database operations
|
||||
are successful, and the error if failure.
|
||||
7. HTTP DELETE requests for http://localhost:3001/deletenote/:noteid. The middleware,
|
||||
should delete the note from the noteList collection according to the noteid carried in the URL.
|
||||
Return an empty string to the client if success and the error if failure.
|
||||
Task 2 Front-end React App
|
||||
Implement the front-end as a React application. The application is accessed at
|
||||
http://localhost:3000/.
|
||||
Preparations
|
||||
Following steps in React_I_examples.pdf, create a React app named noteapp and install the
|
||||
jQuery module in the React app.
|
||||
Implement the React app (noteapp/src/index.js,
|
||||
noteapp/src/App.js, noteapp/src/App.css)
|
||||
index.js (3 marks)
|
||||
Modify the generated Index.js in the ./src folder of your react app directory, which should
|
||||
render the component you create in App.js in the “root” division in the default index.html,
|
||||
and remove any unnecessary code.
|
||||
App.js (50 marks)
|
||||
App.js should import the jQuery module and link to the style sheet App.css.
|
||||
Design and implement the component structure in App.js, such that the front-end page
|
||||
views and functionalities as illustrated in Figures 1-6 can be achieved.
|
||||
Hints:
|
||||
• You can use conditional rendering to decide if the page view in Fig. 1 or Fig. 2 should
|
||||
be rendered. Suppose the root component you are creating in App.js is iNoteApp. In
|
||||
iNoteApp, you may use a state variable to indicate if the user has logged in or not,
|
||||
and then render the component presenting the respective page view accordingly.
|
||||
Initialize the state variable to indicate that no user has logged in, and update it when
|
||||
a user has successfully logged in and logged out, respectively.
|
||||
• In the component implementing the page view as in Fig. 1, add an event handler for
|
||||
the onClick event on the “Sign in” button. When handling the event, send an HTTP
|
||||
POST request for http://localhost:3001/signin (refer to this website for AJAX
|
||||
cross-origin with cookies:
|
||||
http://promincproductions.com/blog/cross-domain-ajax-request-cookies-cors/).
|
||||
According to the response received from the backend service, remain on the page
|
||||
view and display the “Login failure” message at the top of the page, or render the
|
||||
page view as in Fig. 2. You can limit the number of characters in each note title in the
|
||||
left panel to a small fixed number n, i.e., only the first n characters of the note title is
|
||||
shown and the rest shown as “...”.
|
||||
• When handling the onClick event on the “log out” button in a page view as in Figures
|
||||
2-6, send an HTTP GET request for http://localhost:3001/logout and handle the
|
||||
response accordingly.
|
||||
• When handling the onClick event on a node title in the left panel of a page view as in
|
||||
Figures 2-6, send an HTTP GET request for http://localhost:3001/getnote?nodeid=xx,
|
||||
where xx should be the _id of the note that you store with the note title in the list.
|
||||
Then render a page view as in Fig. 5.
|
||||
• When handling the onClick event on the “Save” button in a page view as in Fig. 3,
|
||||
send an HTTP POST request for http://localhost:3001/addnote carrying the new
|
||||
node’s title and content in the body of the request message, and handle the
|
||||
response accordingly.
|
||||
• When handling the onClick event on the “Save” button in a page view as in Fig. 6,
|
||||
send an HTTP PUT request for http://localhost:3001/updatenote/xx where xx should
|
||||
be the _id of the note being updated, and handle the response accordingly.
|
||||
• When handling the onClick event on the “Delete” button in a page view as in Fig. 4
|
||||
or Fig. 5, send an HTTP DELETE request for http://localhost:3001/deletenote/xxx
|
||||
(where xxx is _id of the note to be deleted). If success response is received, update
|
||||
the page view accordingly.
|
||||
• When handling the onKeyUp event (event.key == "Enter") on the search input box in
|
||||
a page view as in Figures 2-6, send an HTTP GET request for
|
||||
http://localhost:3001/searchnotes?searchstr=xxx (where xx is the input search
|
||||
string). When success response is received, update the page view accordingly.
|
||||
App.css (10 marks)
|
||||
Style your page views nicely using CSS rules in App.css.
|
||||
Other marking criteria:
|
||||
(5 marks) Good programming style (avoid redundant code, easy to understand and
|
||||
maintain). You are encouraged to provide a readme.txt file to let us know more about your
|
||||
programs.
|
||||
Submission:
|
||||
You should zip the following files (in the indicated directory structure) into a
|
||||
yourstudentID-a2.zip file
|
||||
NoteService/app.js
|
||||
NoteService /routes/notes.js
|
||||
noteapp/src/App.js
|
||||
noteapp/src/index.js
|
||||
noteapp/src/App.css
|
Reference in New Issue
Block a user