Báo cáo đồ án Công nghệ phần mềm mới "Booking app Mern stack" | Đại học Sư phạm Kỹ thuật Thành phố Hồ Chí Minh

Báo cáo đồ án Công nghệ phần mềm mới "Booking app Mern stack" của Đại học Sư phạm Kỹ thuật Thành phố Hồ Chí Minh với những kiến thức và thông tin bổ ích giúp sinh viên tham khảo, ôn luyện và phục vụ nhu cầu học tập của mình cụ thể là có định hướng ôn tập, nắm vững kiến thức môn học và làm bài tốt trong những bài kiểm tra, bài tiểu luận, bài tập kết thúc học phần, từ đó học tập tốt và có kết quả cao cũng như có thể vận dụng tốt những kiến thức mình đã học vào thực tiễn cuộc sống. Mời bạn đọc đón xem!

 

lOMoARcPSD|36625228
TRƯỜNG ĐẠI HỌC
SƯ PHẠM KỸ THUẬT THÀNH PHỐ HỒ CHÍ MINH
----
----
KHOA CÔNG NGHỆ THÔNG TIN
NGÀNH CÔNG NGHỆ THÔNG TIN
BÁO CÁO ĐỒ ÁN
CÔNG NGHỆ PHẦN MỀM MỚI
BOOKING APP MERN STACK
GVHD: ThS. Nguyn Minh Đo
Sinh viên thực hiện:
Phan Thái Bảo
20110071
Thành ph H Chí Minh, tháng 12 năm 2023
lOMoARcPSD|36625228
Mục lục
Chương 1: Tổng quan chương trình ................................................................................ 1
1.1. Giới thiệu về chương trình: ............................................................................... 1
1.2. Các yêu cầu chức năng: ..................................................................................... 1
1.3. Các yêu cầu phi chức năng ................................................................................ 1
Chương 2. Nội dung..................................................................................................... 2
2.1. Database: ........................................................................................................... 2
2.1.1 Class diagram .................................................................................................. 2
2.1.2 Lược đồ Use Case ........................................................................................... 2
2.1.3 Models ............................................................................................................ 2
2.1.4 Tóm tắt database ............................................................................................. 3
2.2. Backend: ........................................................................................................... 4
2.2.1 Register ........................................................................................................... 4
2.2.2 Login ............................................................................................................... 4
2.2.3 Get profile ....................................................................................................... 5
2.2.4 Log out ............................................................................................................ 5
2.2.5 Upload ảnh bằng link ...................................................................................... 5
2.2.6 Upload ảnh từ máy .......................................................................................... 6
2.2.7 Tạo place ......................................................................................................... 6
2.2.8 Get place của user ........................................................................................... 7
2.2.9 Get place by id ................................................................................................ 7
2.2.10 Chỉnh sửa nơi ............................................................................................. 7
2.2.11 Get place ....................................................................................................... 8
2.2.12 Đặt chỗ .......................................................................................................... 8
2.2.13 Get booking ................................................................................................... 8
2.2.14 Hủy booking .................................................................................................. 9
2.3. FrontEnd ........................................................................................................... 9
2.3.1. Giao diện Trang chủ: ...................................................................................... 9
2.3.2 Giao diện trang Đăng nhập ........................................................................... 10
2.3.3 Giao diện trang đăng ký ................................................................................ 11
2.3.4 Giao diện trang Tài khoản ............................................................................. 12
2.3.5 Giao diện trang chi tiết nơi ở ......................................................................... 14
2.3.6 Giao diện trang Booking ............................................................................... 16
2.3.7 Giao diện trang My Booking ......................................................................... 18
2.3.8 Giao diện trang My accommodations ............................................................... 19
2.3.9 Giao diện trang thêm nơi ở mới ........................................................................ 20
Chương 3. Cài đặt và kiểm thử.......................................................................................23
3.1. Cài đặt:...................................................................................23
3.1.1 Back end:...........................................................................23 3.1.2 Front
end:...........................................................................23
lOMoARcPSD|36625228
3.2. Demo chương trình....................................................................24
Chương 4. Kết Luận.........................................................................................................42
41. Kết Luận:.................................................................................42 4.2. Hướng phát
triển.......................................................................42
lOMoARcPSD|36625228
Chương 1: Tổng quan chương trình
1.1. Giới thiệu về chương trình:
Booking app Airbnb là một nơi giúp chúng ta có thể đặt được những chỗ
yêu thích ở những nơi mà chúng ta muốn đến. Chúng ta có thể lựa chọn
được những nơi ở có giá vừa phải, có thời gian check in, check out và có
giới hạn số người khi ở. Bên cạnh đó nó còn cho phép chúng ta có thể chia
sẻ chỗ ở của mình đến với mọi người
1.2. Các yêu cầu chức năng:
- Người dùng có thể tạo tài khoản với các thông tin cơ bản, đăng nhập vào
hệ thống để có thể tiến hành lựa chọn, đặt chỗ hoặc chia sẽ chỗ ở của
mình
- Người dùng có thể xem chi tiết nơi ở và tiến hành đặt chỗ
- Người dùng có thể xem lại chi tiết đơn đã đặt hoặc hủy đơn
- Người dùng có thể chia sẽ nơi ở của mình
- Người dùng có thể xem và chỉnh sửa nơi ở đã chia sẽ
1.3. Các yêu cầu phi chức năng
- Hệ thống đảm bảo tính dễ sử dụng cho người dùng
- Thông tin mô tả, hình ảnh nơi ở đúng với thực tế
- Website dễ sử dụng cho các nền tảng khác nhau (desktop, mobile,
tablet…)
lOMoARcPSD|36625228
Chương 2. Nội dung
2.1. Database:
2.1.1 Class diagram
2.1.2 Lược đồ Use Case
2.1.3 Models
- User model:
lOMoARcPSD|36625228
const UserSchema = new Schema({
name: String,
email: {type:String, unique:true},
password: String, });
Tạo bảng User với với các trường như name, email, password
- Booking model
const bookingSchema = new mongoose.Schema({
place: {type:mongoose.Schema.Types.ObjectId, required:true, ref:'Place'},
user: {type:mongoose.Schema.Types.ObjectId, required:true},
checkIn: {type:Date, required:true}, checkOut: {type:Date, required:true},
name: {type:String, required:true}, phone: {type:String, required:true}, price:
Number, });
Tạo bảng booking với các trường như place (khóa ngoại đến bảng
Place), user (khóa ngoại đến bảng User), checkIn, checkout, name, phone,
price.
- Place model
const placeSchema = new mongoose.Schema({ owner:
{type:mongoose.Schema.Types.ObjectId, ref:'User'}, title:
String, address: String, photos: [String], description:
String, perks: [String], extraInfo: String,
checkIn: Number,
checkOut: Number,
maxGuests: Number,
price: Number, });
Tạo bảng Place với các trường như owner (khóa ngoại đến bảng User),
title, address, photos, description, perks, extraInfo, checkIn, checkout,
maxGuests, price
2.1.4 Tóm tắt database
Đây là một hệ thống dùng để đặt chỗ ở. Người dùng có thể xem thông
tin phòng, đặt chỗ, hủy chỗ đã đặt đồng thời người dùng cũng có thể chia sẽ
nơi ở của mình đến người khác và có thể chỉnh sửa chỗ ở đã chia sẽ.
Mỗi người dùng có thể đặt nhiều chỗ , những mỗi chỗ đặt chỉ thuộc về
một người dùng (1 – N)
Mỗi nơi ở có thể có nhiều lượt đặt chỗ và mỗi lượt đặt chỗ chỉ thuộc về
một nơi (1 – N)
Mỗi người dùng có thể đặt nhiều nơi và mỗi nơi có thể được đặt bởi
nhiều người dùng (N – N)
lOMoARcPSD|36625228
2.2. Backend:
2.2.1 Register
app.post('/api/register', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const {name,email,password} = req.body;
try {
const userDoc = await User.create({
name, email,
password:bcrypt.hashSync(password, bcryptSalt),
});
res.json(userDoc);
} catch (e) {
res.status(422).json(e);
}
});
Tiến hành tạo mới user với name, email, password từ request gửi đến
(password được mã hóa) sau đó trả về thông tin user được tạo. Nếu lỗi trả về
lỗi 422 và thông tin lỗi
2.2.2 Login
app.post('/api/login', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const {email,password} = req.body; const
userDoc = await User.findOne({email}); if
(userDoc) {
const passOk = bcrypt.compareSync(password, userDoc.password);
if (passOk) { jwt.sign({
email:userDoc.email,
id:userDoc._id
}, jwtSecret, {}, (err,token) => {
if (err) throw err;
res.cookie('token', token).json(userDoc);
});
} else {
res.status(422).json('pass not ok');
}
} else {
res.json('not found');
}
});
Đầu tiên sẽ dùng findOne để tìm email trong bảng user. Nếu có thì sẽ
tiến hành check password xem có đúng không. Nếu password đúng thì dùng
jwt.sign để chuỗi JWT với email và id của user, sau đó gửi token đã tạo đến
lOMoARcPSD|36625228
client thông qua cookie. Nếu password sai thì trả lỗi 422 với nội dung
password không đúng. Nếu không tìm được email thì trả lỗi not found.
2.2.3 Get profile
app.get('/api/profile', (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const {token} = req.cookies;
if (token) { jwt.verify(token, jwtSecret, {}, async (err,
userData) => { if (err) throw err;
const {name,email,_id} = await User.findById(userData.id);
res.json({name,email,_id});
});
} else {
res.json(null);
}
});
Check token nếu có dùng jwt.verify để xác thực token và tiến hành giải
mã token và trả name, email, và id của user đó.
2.2.4 Log out
app.post('/api/logout', (req,res) => {
res.cookie('token', '').json(true); });
Tiến hành xóa token trong cookie
2.2.5 Upload ảnh bằng link
app.post('/api/upload-by-link', async (req,res) => {
const {link} = req.body;
const newName = 'photo' + Date.now() + '.jpg';
await imageDownloader.image({
url: link,
// dest: '/tmp/' +newName,
dest: __dirname + '/uploads/' + newName
});
// const url = await uploadToS3('/tmp/' +newName, newName, mime.lookup('/tmp/' +newName));
// res.json(url);
res.json( 'http://localhost:4000/uploads/' + newName)
});
Dùng imageDownloader để tiến hành download ảnh và lưu lại với tên mới với
đuôi file .jpg, ảnh sẽ được lưu lại ở folder uploads ở phía backend, sau đó trả
về ảnh được upload.
lOMoARcPSD|36625228
2.2.6 Upload ảnh từ máy
const photosMiddleware = multer({dest:'uploads/'});
app.post('/api/upload', photosMiddleware.array('photos', 100), async (req,res) => {
const uploadedFiles = [];
for (let i = 0; i < req.files.length; i++) {
const {path,originalname,mimetype} = req.files[i];
const parts = originalname.split('.')
const ext = parts[parts.length -1]
const newPath = path + "." + ext
fs.renameSync(path, newPath)
// const url = await uploadToS3(path, originalname, mimetype);
uploadedFiles.push(newPath);
}
res.json(uploadedFiles);
});
Dùng mutler để xử lí việc tải ảnh lên từ các tệp tin được gửi từ client
photosMiddleware.array('photos', 100) định nghĩa rằng middleware
này sẽ xử lý tệp tin được gửi dưới dạng một mảng có tên là 'photos', và
tối đa 100 tệp tin có thể được tải lên trong mt lần yêu cầu.
Sau đó tiến hành xử lí các tệp ảnh được gửi đó bằng cách lấy ra tên file
của từng ảnh đó và dùng fs.renameSync để tiến hành đổi tên cho các file đó
và lưu lại vào mảng uploadedFile và trả về các ảnh đó về bên phía client.
2.2.7 Tạo place
app.post('/api/places', (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const {token} = req.cookies;
const {
title,address,addedPhotos,description,price,
perks,extraInfo,checkIn,checkOut,maxGuests,
} = req.body; jwt.verify(token, jwtSecret, {}, async (err,
userData) => { if (err) throw err;
const placeDoc = await Place.create({
owner:userData.id,price,
title,address,photos:addedPhotos,description,
perks,extraInfo,checkIn,checkOut,maxGuests,
});
res.json(placeDoc);
});
});
Đầu tiên sẽ tiến hành xác thực token, sau đó thực hiện việc tạo nơi ở
trong bản Place với các thông tin được gửi từ phía client và trả về thông tin đã
tạo thành công.
lOMoARcPSD|36625228
2.2.8 Get place của user
app.get('/api/user-places', (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const {token} = req.cookies;
jwt.verify(token, jwtSecret, {}, async (err, userData) => {
const {id} = userData;
res.json( await Place.find({owner:id}) );
});
});
Đầu tiên sẽ tiến hành xác thực token, nếu đúng thì sẽ trả về những nơi mà user
đó đã tạo thông qua userId của họ.
2.2.9 Get place by id
app.get('/api/places/:id', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const {id} = req.params;
res.json(await Place.findById(id));
});
Dùng findById để trả về nơi theo id được truyền từ client.
2.2.10 Chỉnh sửa nơi
app.put('/api/places', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const {token} = req.cookies;
const {
id, title,address,addedPhotos,description,
perks,extraInfo,checkIn,checkOut,maxGuests,price,
} = req.body;
jwt.verify(token, jwtSecret, {}, async (err, userData) => {
if (err) throw err;
const placeDoc = await Place.findById(id); if
(userData.id === placeDoc.owner.toString()) {
placeDoc.set({
title,address,photos:addedPhotos,description,
perks,extraInfo,checkIn,checkOut,maxGuests,price,
});
await placeDoc.save();
res.json('ok');
}
});
});
Tiến hành xác thực token, nếu đúng thì sẽ dùng findById để tìm đến
nơi ở đó theo id, sau đó kiểm tra thông tin id của user đang cần chỉnh sửa và
id của người tạo xem đúng không, nếu đúng thì sẽ tiến hành lưu lại các thông
tin cần chỉnh sửa và trả về kết quả cập nhật thành công.
lOMoARcPSD|36625228
2.2.11 Get place
app.get('/api/places', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
res.json( await Place.find() );
});
Dùng find() để lấy ra được tất cả các nơi trong bảng Place
2.2.12 Đặt chỗ
app.post('/api/bookings', async (req, res) => {
mongoose.connect(process.env.MONGO_URL);
const userData = await getUserDataFromReq(req);
const {
place,checkIn,checkOut,numberOfGuests,name,phone,price,
} = req.body; Booking.create({
place,checkIn,checkOut,numberOfGuests,name,phone,price,
user:userData.id, }).then((doc) => { res.json(doc);
}).catch((err) => { throw err;
});
});
Tiến hành xác thực token để lấy user id, sau đó tiến hành tạo đơn trong bảng
Booking với dữ liệu được truyền từ bên phía client và trả về thông tin đơn đã
được đặt thành công.
2.2.13 Get booking
app.get('/api/bookings', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const userData = await getUserDataFromReq(req);
res.json( await Booking.find({user:userData.id}).populate('place') );
});
Tiến hành xác thực token và trả về thông tin booking trong bảng Booking
thông qua id của user.
lOMoARcPSD|36625228
2.2.14 Hủy booking
app.delete('/api/bookings/:id', async (req, res) => {
mongoose.connect(process.env.MONGO_URL);
const bookingId = req.params.id;
const booking = await Booking.findById(bookingId);
try {
const booking = await Booking.findById(bookingId);
if (!booking) {
return res.status(404).json({ message: 'Booking not found' });
}
await Booking.findByIdAndDelete(bookingId);
res.json({ message: 'Booking deleted successfully' });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Internal server error' });
}
});
Tìm đơn hàng trong bảng Booking thông qua id được truyền từ client.
Sau đó tiến hành check xem có id đó không, nếu không thì thông báo lỗi
booking not found, nếu có thì dùng findByIdAndDelete để tiến hành xóa dữ
liệu trong bảng Booking thông qua id đó và trả về xóa thành công.
2.3. FrontEnd
2.3.1. Giao diện Trang chủ:
- Gọi api get places để lấy ra tất cả các phòng trong hệ thống sau đó hiển thị
lên trang người dùng
lOMoARcPSD|36625228
2.3.2 Giao diện trang Đăng nhập
- Gọi api login và truyền vào thông tin đăng nhập để tiến hành đăng nhập, sau
đó dùng UseContext để tiến hành lưu trữ thông tin đăng nhập và hiển thị tên
người dùng trên trang web
async function handleLoginSubmit(ev) {
ev.preventDefault(); try {
const {data} = await axios.post('/login', {email,password});
setUser(data);
alert('Login successful');
setRedirect(true);
} catch (e) {
alert('Login failed');
}
}
if (redirect) {
lOMoARcPSD|36625228
return <Navigate to={'/'} /> }
Đăng nhập thành công thì chuyển đến trang chủ
2.3.3 Giao diện trang đăng ký
- Gọi api register và truyền vào thông tin đăng kí để tiến hành đăng kí, nếu
đăng kí thành công thì sẽ chuyển đến trang đăng nhập, sai thì sẽ thông báo lỗi
async function registerUser(ev) {
ev.preventDefault(); try {
await axios.post('/register', {
name,
email,
password,
});
lOMoARcPSD|36625228
alert('Registration successful. Now you can log in');
setRedirect(true);
} catch (e) {
alert('Registration failed. Please try again later');
}
}
if (redirect) {
return <Navigate to={"/login"} />; }
2.3.4 Giao diện trang Tài khoản
Dùng UserContext để hiển thị thông tin user và gọi api logout nếu
muốn đăng xuất. Nếu chưa đăng nhập thì sẽ chuyển đến trang login
const [redirect,setRedirect] = useState(null); const {ready,user,setUser} =
useContext(UserContext);
let {subpage} = useParams();
lOMoARcPSD|36625228
if (subpage === undefined) {
subpage = 'profile'; }
async function logout() {
await axios.post('/logout');
setRedirect('/');
setUser(null); }
// if (!ready) {
// return 'Loading...'; // }
if (ready && !user && !redirect) {
return <Navigate to={'/login'} /> }
if (redirect) {
return <Navigate to={redirect} /> }
Trong UseContext gọi api get profile để lấy ra thông tin user
useEffect(() => {
if (!user) {
axios.get('/profile').then(({data}) => {
setUser(data);
setReady(true);
});
}
}, []);
return (
<UserContext.Provider value={{user,setUser,ready}}>
{children}
</UserContext.Provider>
);
lOMoARcPSD|36625228
2.3.5 Giao diện trang chi tiết nơi ở
- Gọi api get place by id và truyền id của phòng được chọn để lấy được
thông tin chi tiết của phòng đó và hiển thị lên trên giao diện
useEffect(() => {
if (!id) {
return;
}
axios.get(`/places/${id}`).then(response => { setPlace(response.data);
});
}, [id]);
if (!place) return '';
lOMoARcPSD|36625228
lOMoARcPSD|36625228
- Gọi api booking và truyền thời gian, số lượng người, tên, số điện thoại
để tiến hành đặt phòng
async function bookThisPlace() {
const response = await axios.post('/bookings', {
checkIn,checkOut,numberOfGuests,name,phone,
place:place._id,
price:numberOfNights * place.price,
});
const bookingId = response.data._id;
setRedirect(`/account/bookings/${bookingId}`); }
2.3.6 Giao diện trang Booking
- Gọi api get booking và tìm đến đơn có id bằng id trên url để lấy ra được
thông tin chi tết của đơn hàng đó
useEffect(() => {
if (id) {
axios.get('/bookings').then(response => {
const foundBooking = response.data.find(({_id}) => _id === id);
if (foundBooking) {
setBooking(foundBooking);
} });
}
}, [id]);
- Gọi api delete booking và truyền id vào để tiến hành xóa đơn hàng
lOMoARcPSD|36625228
const handleDeleteOrder = async() => { const userConfirmed
= window.confirm(
"Bn có chAc muốAn huCy đơn đặt phòng khng?" );
// NếAu người dùng đã xác nhận, thc hin xóa
if (userConfirmed && id) {
axios
.delete(`/bookings/${id}`)
.then((response) => {
if (response?.data.message === "Booking deleted successfully") {
alert("Đã xóa đơn đặt phòng thành cng!");
window.location.reload();
}
})
.catch((error) => {
console.error("Error deleting order:", error); alert("Đã xaCy ra lốTi khi xóa
đơn đặt phòng.");
});
}
}
lOMoARcPSD|36625228
2.3.7 Giao diện trang My Booking
- Gọi api get booking để lấy ra toàn bộ đơn hàng và hiển thị lên giao diện
useEffect(() => {
axios.get('/bookings').then(response => {
setBookings(response.data);
});
}, []);
lOMoARcPSD|36625228
2.3.8 Giao diện trang My accommodations
- Gọi api get user-place để lấy được tất cả nơi mà user này đã đăng
useEffect(() => {
axios.get('/user-places').then(({data}) => {
setPlaces(data);
});
}, []);
lOMoARcPSD|36625228
2.3.9 Giao diện trang thêm nơi ở mới
- Gọi api get place by id để tiến hành truyền dữ liệu vào các ô input để tiến
hành update dữ liệu
useEffect(() => { if (!id) {
return;
lOMoARcPSD| 36625228
}
axios.get('/places/'+id).then(response => {
const {data} = response;
setTitle(data.title);
setAddress(data.address);
setAddedPhotos(data.photos);
setDescription(data.description);
setPerks(data.perks);
setExtraInfo(data.extraInfo);
setCheckIn(data.checkIn);
setCheckOut(data.checkOut);
setMaxGuests(data.maxGuests);
setPrice(data.price);
}); }, [id]);
async function savePlace(ev) {
ev.preventDefault();
const placeData = {
title, address, addedPhotos,
description, perks, extraInfo,
checkIn, checkOut, maxGuests, price,
};
if (id) {
// update
await axios.put('/places', {
id, ...placeData
});
setRedirect(true);
} else {
// new place
await axios.post('/places', placeData);
setRedirect(true);
}
}
Nếu tạo mới thì sẽ gọi api post places để tạo nơi ở mới
- Ở phần hình ảnh: gọi api upload by link nếu tải ảnh bằng link và gọi api
upload nếu tải ảnh từ thiết b
lOMoARcPSD|36625228
async function addPhotoByLink(ev) {
ev.preventDefault();
const {data:filename} = await axios.post('/upload-by-link',
{link: photoLink});
onChange(prev => { if
(filename) {
return [...prev, filename];
}
return prev;
});
setPhotoLink('');
}
function uploadPhoto(ev) { const files = ev.target.files;
const data = new FormData();
for (let i = 0; i < files.length; i++) {
data.append('photos', files[i]);
}
axios.post('/upload', data, {
headers: {'Content-type':'multipart/form-data'}
}).then(response => {
const {data:filenames} = response;
onChange(prev => {
return [...prev, ...filenames];
});
})
}
Dùng 'Content-type':'multipart/form-data' để có thể gửi nhiều file cùng lúc
lOMoARcPSD|36625228
lOMoARcPSD|36625228
Chương 3. Cài đặt và kiểm thử
3.1. Cài đặt:
3.1.1 Back end:
- Bước 1: Mở terminal và cd đến folder api
- Bước 2: Tạo mt .env với nội dung là đường link kết nối với mongoodb
MONGO_URL = mongodb+srv://booking:gvF7bIzlGMh40ptv@cluster0.jhiwsro.mongodb.net/?
retryWrites=true&w=majority
- Bước 3 : Tạo folder uploads trong folder api
- Bước 4: chạy câu lệnh npm install để download node modules- Bước 5:
chạy câu lệnh npm start để khởi động
3.1.2 Front end:
- Bước 1: Mở terminal và cd đến folder client
- Bước 2: Tạo file .evn với nội dung là url của backend
VITE_API_BASE_URL = http://localhost:4000/api
lOMoARcPSD|36625228
- Bước 3: Chạy câu lệnh npm install để download node modules
- Bước 4: Chạy câu lệnh npm run dev để khởi động chương trình
- Bước 5: Truy cập đường dẫn http://localhost:5173/
3.2. Demo chương trình
- Trang chủ:
- Đăng ký: Chọn vào avatar phía trên góc phải ở trang chủ để chuyển đến
trang đăng nhập. Sau đó chọn “Register now” để chuyển đến trang đăng ký
và nhập thông tin đăng ký rồi nhấn “Register”
lOMoARcPSD|36625228
Sau khi đăng kí thành công trang sẽ chuyển đến trang đăng nhập
- Đăng nhập: Chọn vào avatar phía trên góc phải ở trang chủ để chuyển đến
trang đăng nhập. Sau đó nhập thông tin đăng nhập và bấm “Login”
lOMoARcPSD|36625228
Sau khi đăng nhập thành công thì sẽ chuyển đến trang chủ
lOMoARcPSD|36625228
- Xem thông tin chi tiết của từng phòng: Ở trang chủ, ta sẽ chọn phòng mà
chúng ta muốn xem. Trang web sẽ chuyển đến trang xem thông tin chi tiết
của phòng đó và hiển thị tất cả thông tin của phòng đó bao gồm hình ảnh,
mô tả, vị trí, giá, ….
lOMoARcPSD|36625228
- Booking: Khi chọn được phòng mong muốn, người dùng sẽ tiến hành nhập
thời gian checkIn, checkOut, số lượng người, tên, số điện thoại và nhấn
“Booking this place” để đặt hàng
lOMoARcPSD|36625228
Sau khi đặt hàng thành công thì trang sẽ chuyển đến trang chi tiết đơn hàng
lOMoARcPSD|36625228
- Hủy đơn đặt hàng: Khi booking thành công thì trang sẽ chuyển đến chi tiết
đơn hàng, nếu muốn hủy đơn thì nhấn chọn “Cancel order” và xác nhận
hủy. Hoặc người dùng có thể chọn vào tên của mìnhphía trên bên phải
trang web và chọn vào “My booking” sau đó chọn đến nơi mà bạn muốn
hủy nhấn chọn vào nơi đó và nhấn “Cancel order” và xác nhận hủy.
lOMoARcPSD|36625228
lOMoARcPSD|36625228
lOMoARcPSD|36625228
- Xem và chỉnh sửa các nơi đã đăng: Chọn vào tên người dùng ở phía trên
bên phải trang web và chọn “My accommodation” để xem tất cả các nơi đã
đăng
lOMoARcPSD|36625228
Nếu muốn chỉnh thử thông tin của nơi nào thì chọn vào nơi đó trang web sẽ
chuyển đến trang cập nhật và nhập nội dung cần chỉnh sửa vào và nhấn
“Save”
lOMoARcPSD|36625228
lOMoARcPSD|36625228
- Thêm nơi ở mới: Chọn vào “Add new place” ở trong “My
accommodation” sao đó nhập tất cả các thông tin cần thiết và hình ảnh(có
thể tải bằng link hoặc từ thiết bị cá nhân) và nhấn “Save” để đăng nơi ở
mới.
lOMoARcPSD|36625228
Nếu upload ảnh bằng link thì sẽ dán link ảnh vào và nhấn “Add photo”
lOMoARcPSD|36625228
Nếu tải ảnh từ thiết bị thì nhấn chọn “Upload” và tải ảnh lên và có thể tải lên
nhiều ảnh cung lúc
lOMoARcPSD|36625228
Thêm các thông tin cần thiết và giá phòng vào và nhấn “Save”
lOMoARcPSD|36625228
Nơi ở mới đã được thêm
- Đăng xuất: Chọn “Log out” trong My profile để tiến hành đăng xuất
lOMoARcPSD|36625228
lOMoARcPSD| 36625228
Chương 4. Kết Luận
41. Kết Luận:
Về cơ bản, em tự nhận xét rằng em đã cơ bản hoàn thành phần yêu cầu đề ra,
và sau đây là một số ưu điểm và nhược điểm rút ra từ web Booking App:
- Về ưu điểm:
+ Giao diện dễ sử dụng.
+ Các chức năng được thể hiện rõ ràng.
+ Chương trình nhẹ, hoạt động mượt mà.
Về nhược điểm
+ Các chức năng còn cơ bản và chưa thật sự được tối ưu.
+ Thiết kế còn đơn giản.
4.2. Hướng phát triển
- Hoàn thiện các tính năng.
- Upload ảnh lên cloud.
- Cải thiện trải nghiệm người dùng.
- Thêm nhiều tính năng mới hơn để hoàn thiện tốt nhất trang web.
- Phát triển giao diện đẹp mắt hơn.
| 1/46

Preview text:

lOMoARcPSD| 36625228 TRƯỜNG ĐẠI HỌC
SƯ PHẠM KỸ THUẬT THÀNH PHỐ HỒ CHÍ MINH ---- ----
HCMC University of Technology and Education KHOA CÔNG NGHỆ THÔNG TIN
NGÀNH CÔNG NGHỆ THÔNG TIN BÁO CÁO ĐỒ ÁN
CÔNG NGHỆ PHẦN MỀM MỚI BOOKING APP MERN STACK
GVHD: ThS. Nguyễn Minh Đạo
Sinh viên thực hiện: Phan Thái Bảo 20110071
Thành phố Hồ Chí Minh, tháng 12 năm 2023 lOMoARcPSD| 36625228 Mục lục
Chương 1: Tổng quan chương trình ................................................................................ 1
1.1. Giới thiệu về chương trình: ............................................................................... 1
1.2. Các yêu cầu chức năng: ..................................................................................... 1
1.3. Các yêu cầu phi chức năng ................................................................................ 1
Chương 2. Nội dung..................................................................................................... 2
2.1. Database: ........................................................................................................... 2
2.1.1 Class diagram .................................................................................................. 2
2.1.2 Lược đồ Use Case ........................................................................................... 2
2.1.3 Models ............................................................................................................ 2
2.1.4 Tóm tắt database ............................................................................................. 3
2.2. Backend: ........................................................................................................... 4
2.2.1 Register ........................................................................................................... 4
2.2.2 Login ............................................................................................................... 4
2.2.3 Get profile ....................................................................................................... 5
2.2.4 Log out ............................................................................................................ 5
2.2.5 Upload ảnh bằng link ...................................................................................... 5
2.2.6 Upload ảnh từ máy .......................................................................................... 6
2.2.7 Tạo place ......................................................................................................... 6
2.2.8 Get place của user ........................................................................................... 7
2.2.9 Get place by id ................................................................................................ 7
2.2.10 Chỉnh sửa nơi ở ............................................................................................. 7
2.2.11 Get place ....................................................................................................... 8
2.2.12 Đặt chỗ .......................................................................................................... 8
2.2.13 Get booking ................................................................................................... 8
2.2.14 Hủy booking .................................................................................................. 9
2.3. FrontEnd ........................................................................................................... 9
2.3.1. Giao diện Trang chủ: ...................................................................................... 9
2.3.2 Giao diện trang Đăng nhập ........................................................................... 10
2.3.3 Giao diện trang đăng ký ................................................................................ 11
2.3.4 Giao diện trang Tài khoản ............................................................................. 12
2.3.5 Giao diện trang chi tiết nơi ở ......................................................................... 14
2.3.6 Giao diện trang Booking ............................................................................... 16
2.3.7 Giao diện trang My Booking ......................................................................... 18
2.3.8 Giao diện trang My accommodations ............................................................... 19
2.3.9 Giao diện trang thêm nơi ở mới ........................................................................ 20
Chương 3. Cài đặt và kiểm thử.......................................................................................23
3.1. Cài đặt:...................................................................................23
3.1.1 Back end:...........................................................................23 3.1.2 Front
end:...........................................................................23 lOMoARcPSD| 36625228
3.2. Demo chương trình....................................................................24
Chương 4. Kết Luận.........................................................................................................42
41. Kết Luận:.................................................................................42 4.2. Hướng phát
triển.......................................................................42 lOMoARcPSD| 36625228
Chương 1: Tổng quan chương trình
1.1. Giới thiệu về chương trình:
Booking app Airbnb là một nơi giúp chúng ta có thể đặt được những chỗ
yêu thích ở những nơi mà chúng ta muốn đến. Chúng ta có thể lựa chọn
được những nơi ở có giá vừa phải, có thời gian check in, check out và có
giới hạn số người khi ở. Bên cạnh đó nó còn cho phép chúng ta có thể chia
sẻ chỗ ở của mình đến với mọi người
1.2. Các yêu cầu chức năng:
- Người dùng có thể tạo tài khoản với các thông tin cơ bản, đăng nhập vào
hệ thống để có thể tiến hành lựa chọn, đặt chỗ hoặc chia sẽ chỗ ở của mình
- Người dùng có thể xem chi tiết nơi ở và tiến hành đặt chỗ
- Người dùng có thể xem lại chi tiết đơn đã đặt hoặc hủy đơn
- Người dùng có thể chia sẽ nơi ở của mình
- Người dùng có thể xem và chỉnh sửa nơi ở đã chia sẽ
1.3. Các yêu cầu phi chức năng
- Hệ thống đảm bảo tính dễ sử dụng cho người dùng
- Thông tin mô tả, hình ảnh nơi ở đúng với thực tế
- Website dễ sử dụng cho các nền tảng khác nhau (desktop, mobile, tablet…) lOMoARcPSD| 36625228 Chương 2. Nội dung 2.1. Database: 2.1.1 Class diagram
2.1.2 Lược đồ Use Case 2.1.3 Models - User model: lOMoARcPSD| 36625228
const UserSchema = new Schema({ name: String,
email: {type:String, unique:true}, password: String, });
Tạo bảng User với với các trường như name, email, password - Booking model
const bookingSchema = new mongoose.Schema({
place: {type:mongoose.Schema.Types.ObjectId, required:true, ref:'Place'},
user: {type:mongoose.Schema.Types.ObjectId, required:true},
checkIn: {type:Date, required:true}, checkOut: {type:Date, required:true},
name: {type:String, required:true}, phone: {type:String, required:true}, price: Number, });
Tạo bảng booking với các trường như place (khóa ngoại đến bảng
Place), user (khóa ngoại đến bảng User), checkIn, checkout, name, phone, price. - Place model
const placeSchema = new mongoose.Schema({ owner:
{type:mongoose.Schema.Types.ObjectId, ref:'User'}, title:
String, address: String, photos: [String], description:
String, perks: [String], extraInfo: String, checkIn: Number, checkOut: Number, maxGuests: Number, price: Number, });
Tạo bảng Place với các trường như owner (khóa ngoại đến bảng User),
title, address, photos, description, perks, extraInfo, checkIn, checkout, maxGuests, price
2.1.4 Tóm tắt database
Đây là một hệ thống dùng để đặt chỗ ở. Người dùng có thể xem thông
tin phòng, đặt chỗ, hủy chỗ đã đặt đồng thời người dùng cũng có thể chia sẽ
nơi ở của mình đến người khác và có thể chỉnh sửa chỗ ở đã chia sẽ.
Mỗi người dùng có thể đặt nhiều chỗ , những mỗi chỗ đặt chỉ thuộc về
một người dùng (1 – N)
Mỗi nơi ở có thể có nhiều lượt đặt chỗ và mỗi lượt đặt chỗ chỉ thuộc về một nơi (1 – N)
Mỗi người dùng có thể đặt nhiều nơi và mỗi nơi có thể được đặt bởi
nhiều người dùng (N – N) lOMoARcPSD| 36625228 2.2. Backend: 2.2.1 Register
app.post('/api/register', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const {name,email,password} = req.body; try {
const userDoc = await User.create({ name, email,
password:bcrypt.hashSync(password, bcryptSalt), }); res.json(userDoc); } catch (e) { res.status(422).json(e); } });
Tiến hành tạo mới user với name, email, password từ request gửi đến
(password được mã hóa) sau đó trả về thông tin user được tạo. Nếu lỗi trả về
lỗi 422 và thông tin lỗi 2.2.2 Login
app.post('/api/login', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const {email,password} = req.body; const
userDoc = await User.findOne({email}); if (userDoc) {
const passOk = bcrypt.compareSync(password, userDoc.password); if (passOk) { jwt.sign({ email:userDoc.email, id:userDoc._id
}, jwtSecret, {}, (err,token) => { if (err) throw err;
res.cookie('token', token).json(userDoc); }); } else {
res.status(422).json('pass not ok'); } } else { res.json('not found'); } });
Đầu tiên sẽ dùng findOne để tìm email trong bảng user. Nếu có thì sẽ
tiến hành check password xem có đúng không. Nếu password đúng thì dùng
jwt.sign để chuỗi JWT với email và id của user, sau đó gửi token đã tạo đến lOMoARcPSD| 36625228
client thông qua cookie. Nếu password sai thì trả lỗi 422 với nội dung
password không đúng. Nếu không tìm được email thì trả lỗi not found. 2.2.3 Get profile
app.get('/api/profile', (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;
if (token) { jwt.verify(token, jwtSecret, {}, async (err,
userData) => { if (err) throw err;
const {name,email,_id} = await User.findById(userData.id); res.json({name,email,_id}); }); } else { res.json(null); } });
Check token nếu có dùng jwt.verify để xác thực token và tiến hành giải
mã token và trả name, email, và id của user đó. 2.2.4 Log out
app.post('/api/logout', (req,res) => {
res.cookie('token', '').json(true); });
Tiến hành xóa token trong cookie
2.2.5 Upload ảnh bằng link
app.post('/api/upload-by-link', async (req,res) => { const {link} = req.body;
const newName = 'photo' + Date.now() + '.jpg';
await imageDownloader.image({ url: link, // dest: '/tmp/' +newName,
dest: __dirname + '/uploads/' + newName });
// const url = await uploadToS3('/tmp/' +newName, newName, mime.lookup('/tmp/' +newName)); // res.json(url);
res.json( 'http://localhost:4000/uploads/' + newName) });
Dùng imageDownloader để tiến hành download ảnh và lưu lại với tên mới với
đuôi file .jpg, ảnh sẽ được lưu lại ở folder uploads ở phía backend, sau đó trả về ảnh được upload. lOMoARcPSD| 36625228
2.2.6 Upload ảnh từ máy
const photosMiddleware = multer({dest:'uploads/'});
app.post('/api/upload', photosMiddleware.array('photos', 100), async (req,res) => { const uploadedFiles = [];
for (let i = 0; i < req.files.length; i++) {
const {path,originalname,mimetype} = req.files[i];
const parts = originalname.split('.')
const ext = parts[parts.length -1]
const newPath = path + "." + ext fs.renameSync(path, newPath)
// const url = await uploadToS3(path, originalname, mimetype); uploadedFiles.push(newPath); } res.json(uploadedFiles); });
Dùng mutler để xử lí việc tải ảnh lên từ các tệp tin được gửi từ client
photosMiddleware.array('photos', 100) định nghĩa rằng middleware
này sẽ xử lý tệp tin được gửi dưới dạng một mảng có tên là 'photos', và
tối đa 100 tệp tin có thể được tải lên trong một lần yêu cầu.
Sau đó tiến hành xử lí các tệp ảnh được gửi đó bằng cách lấy ra tên file
của từng ảnh đó và dùng fs.renameSync để tiến hành đổi tên cho các file đó
và lưu lại vào mảng uploadedFile và trả về các ảnh đó về bên phía client. 2.2.7 Tạo place
app.post('/api/places', (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies; const {
title,address,addedPhotos,description,price,
perks,extraInfo,checkIn,checkOut,maxGuests,
} = req.body; jwt.verify(token, jwtSecret, {}, async (err,
userData) => { if (err) throw err;
const placeDoc = await Place.create({ owner:userData.id,price,
title,address,photos:addedPhotos,description,
perks,extraInfo,checkIn,checkOut,maxGuests, }); res.json(placeDoc); }); });
Đầu tiên sẽ tiến hành xác thực token, sau đó thực hiện việc tạo nơi ở
trong bản Place với các thông tin được gửi từ phía client và trả về thông tin đã tạo thành công. lOMoARcPSD| 36625228
2.2.8 Get place của user
app.get('/api/user-places', (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;
jwt.verify(token, jwtSecret, {}, async (err, userData) => { const {id} = userData;
res.json( await Place.find({owner:id}) ); }); });
Đầu tiên sẽ tiến hành xác thực token, nếu đúng thì sẽ trả về những nơi mà user
đó đã tạo thông qua userId của họ. 2.2.9 Get place by id
app.get('/api/places/:id', async (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {id} = req.params;
res.json(await Place.findById(id)); });
Dùng findById để trả về nơi theo id được truyền từ client.
2.2.10 Chỉnh sửa nơi ở
app.put('/api/places', async (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies; const {
id, title,address,addedPhotos,description,
perks,extraInfo,checkIn,checkOut,maxGuests,price, } = req.body;
jwt.verify(token, jwtSecret, {}, async (err, userData) => { if (err) throw err;
const placeDoc = await Place.findById(id); if
(userData.id === placeDoc.owner.toString()) { placeDoc.set({
title,address,photos:addedPhotos,description,
perks,extraInfo,checkIn,checkOut,maxGuests,price, }); await placeDoc.save(); res.json('ok'); } }); });
Tiến hành xác thực token, nếu đúng thì sẽ dùng findById để tìm đến
nơi ở đó theo id, sau đó kiểm tra thông tin id của user đang cần chỉnh sửa và
id của người tạo xem đúng không, nếu đúng thì sẽ tiến hành lưu lại các thông
tin cần chỉnh sửa và trả về kết quả cập nhật thành công. lOMoARcPSD| 36625228 2.2.11 Get place
app.get('/api/places', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
res.json( await Place.find() ); });
Dùng find() để lấy ra được tất cả các nơi trong bảng Place 2.2.12 Đặt chỗ
app.post('/api/bookings', async (req, res) => {
mongoose.connect(process.env.MONGO_URL);
const userData = await getUserDataFromReq(req); const {
place,checkIn,checkOut,numberOfGuests,name,phone,price,
} = req.body; Booking.create({
place,checkIn,checkOut,numberOfGuests,name,phone,price,
user:userData.id, }).then((doc) => { res.json(doc);
}).catch((err) => { throw err; }); });
Tiến hành xác thực token để lấy user id, sau đó tiến hành tạo đơn trong bảng
Booking với dữ liệu được truyền từ bên phía client và trả về thông tin đơn đã được đặt thành công. 2.2.13 Get booking
app.get('/api/bookings', async (req,res) => {
mongoose.connect(process.env.MONGO_URL);
const userData = await getUserDataFromReq(req);
res.json( await Booking.find({user:userData.id}).populate('place') ); });
Tiến hành xác thực token và trả về thông tin booking trong bảng Booking thông qua id của user. lOMoARcPSD| 36625228 2.2.14 Hủy booking
app.delete('/api/bookings/:id', async (req, res) => {
mongoose.connect(process.env.MONGO_URL);
const bookingId = req.params.id;
const booking = await Booking.findById(bookingId); try {
const booking = await Booking.findById(bookingId); if (!booking) {
return res.status(404).json({ message: 'Booking not found' }); }
await Booking.findByIdAndDelete(bookingId);
res.json({ message: 'Booking deleted successfully' }); } catch (error) { console.error(error);
res.status(500).json({ message: 'Internal server error' }); } });
Tìm đơn hàng trong bảng Booking thông qua id được truyền từ client.
Sau đó tiến hành check xem có id đó không, nếu không thì thông báo lỗi
booking not found, nếu có thì dùng findByIdAndDelete để tiến hành xóa dữ
liệu trong bảng Booking thông qua id đó và trả về xóa thành công. 2.3. FrontEnd
2.3.1. Giao diện Trang chủ:
- Gọi api get places để lấy ra tất cả các phòng trong hệ thống sau đó hiển thị lên trang người dùng lOMoARcPSD| 36625228
2.3.2 Giao diện trang Đăng nhập
- Gọi api login và truyền vào thông tin đăng nhập để tiến hành đăng nhập, sau
đó dùng UseContext để tiến hành lưu trữ thông tin đăng nhập và hiển thị tên
người dùng trên trang web
async function handleLoginSubmit(ev) { ev.preventDefault(); try {
const {data} = await axios.post('/login', {email,password}); setUser(data); alert('Login successful'); setRedirect(true); } catch (e) { alert('Login failed'); } } if (redirect) { lOMoARcPSD| 36625228 return }
Đăng nhập thành công thì chuyển đến trang chủ
2.3.3 Giao diện trang đăng ký
- Gọi api register và truyền vào thông tin đăng kí để tiến hành đăng kí, nếu
đăng kí thành công thì sẽ chuyển đến trang đăng nhập, sai thì sẽ thông báo lỗi
async function registerUser(ev) { ev.preventDefault(); try {
await axios.post('/register', { name, email, password, }); lOMoARcPSD| 36625228
alert('Registration successful. Now you can log in'); setRedirect(true); } catch (e) {
alert('Registration failed. Please try again later'); } } if (redirect) { return ; }
2.3.4 Giao diện trang Tài khoản
Dùng UserContext để hiển thị thông tin user và gọi api logout nếu
muốn đăng xuất. Nếu chưa đăng nhập thì sẽ chuyển đến trang login
const [redirect,setRedirect] = useState(null); const {ready,user,setUser} = useContext(UserContext); let {subpage} = useParams(); lOMoARcPSD| 36625228 if (subpage === undefined) { subpage = 'profile'; } async function logout() { await axios.post('/logout'); setRedirect('/'); setUser(null); } // if (!ready) { // return 'Loading...'; // }
if (ready && !user && !redirect) { return } if (redirect) { return }
Trong UseContext gọi api get profile để lấy ra thông tin user useEffect(() => { if (!user) {
axios.get('/profile').then(({data}) => { setUser(data); setReady(true); }); } }, []); return ( {children} ); lOMoARcPSD| 36625228
2.3.5 Giao diện trang chi tiết nơi ở -
Gọi api get place by id và truyền id của phòng được chọn để lấy được
thông tin chi tiết của phòng đó và hiển thị lên trên giao diện useEffect(() => { if (!id) { return; }
axios.get(`/places/${id}`).then(response => { setPlace(response.data); }); }, [id]); if (!place) return ''; lOMoARcPSD| 36625228 lOMoARcPSD| 36625228 -
Gọi api booking và truyền thời gian, số lượng người, tên, số điện thoại
để tiến hành đặt phòng
async function bookThisPlace() {
const response = await axios.post('/bookings', {
checkIn,checkOut,numberOfGuests,name,phone, place:place._id,
price:numberOfNights * place.price, });
const bookingId = response.data._id;
setRedirect(`/account/bookings/${bookingId}`); }
2.3.6 Giao diện trang Booking
- Gọi api get booking và tìm đến đơn có id bằng id trên url để lấy ra được
thông tin chi tết của đơn hàng đó useEffect(() => { if (id) {
axios.get('/bookings').then(response => {
const foundBooking = response.data.find(({_id}) => _id === id); if (foundBooking) { setBooking(foundBooking); } }); } }, [id]);
- Gọi api delete booking và truyền id vào để tiến hành xóa đơn hàng lOMoARcPSD| 36625228
const handleDeleteOrder = async() => { const userConfirmed = window.confirm(
"Bạn có chắAc muốAn huCy đơn đặt phòng khống?" );
// NếAu người dùng đã xác nhận, thực hiện xóa
if (userConfirmed && id) { axios .delete(`/bookings/${id}`) .then((response) => {
if (response?.data.message === "Booking deleted successfully") {
alert("Đã xóa đơn đặt phòng thành cống!"); window.location.reload(); } }) .catch((error) => {
console.error("Error deleting order:", error); alert("Đã xaCy ra lốTi khi xóa đơn đặt phòng."); }); } } lOMoARcPSD| 36625228
2.3.7 Giao diện trang My Booking
- Gọi api get booking để lấy ra toàn bộ đơn hàng và hiển thị lên giao diện useEffect(() => {
axios.get('/bookings').then(response => { setBookings(response.data); }); }, []); lOMoARcPSD| 36625228
2.3.8 Giao diện trang My accommodations
- Gọi api get user-place để lấy được tất cả nơi mà user này đã đăng useEffect(() => {
axios.get('/user-places').then(({data}) => { setPlaces(data); }); }, []); lOMoARcPSD| 36625228
2.3.9 Giao diện trang thêm nơi ở mới
- Gọi api get place by id để tiến hành truyền dữ liệu vào các ô input để tiến hành update dữ liệu
useEffect(() => { if (!id) { return; lOMoAR cPSD| 36625228 }
axios.get('/places/'+id).then(response => { const {data} = response; setTitle(data.title); setAddress(data.address); setAddedPhotos(data.photos);
setDescription(data.description); setPerks(data.perks);
setExtraInfo(data.extraInfo); setCheckIn(data.checkIn); setCheckOut(data.checkOut);
setMaxGuests(data.maxGuests); setPrice(data.price); }); }, [id]);
async function savePlace(ev) { ev.preventDefault(); const placeData = { title, address, addedPhotos,
description, perks, extraInfo,
checkIn, checkOut, maxGuests, price, }; if (id) { // update await axios.put('/places', { id, ...placeData }); setRedirect(true); } else { // new place
await axios.post('/places', placeData); setRedirect(true); } }
Nếu tạo mới thì sẽ gọi api post places để tạo nơi ở mới
- Ở phần hình ảnh: gọi api upload by link nếu tải ảnh bằng link và gọi api
upload nếu tải ảnh từ thiết bị lOMoARcPSD| 36625228
async function addPhotoByLink(ev) { ev.preventDefault();
const {data:filename} = await axios.post('/upload-by-link', {link: photoLink}); onChange(prev => { if (filename) { return [...prev, filename]; } return prev; }); setPhotoLink(''); }
function uploadPhoto(ev) { const files = ev.target.files; const data = new FormData();
for (let i = 0; i < files.length; i++) {
data.append('photos', files[i]); }
axios.post('/upload', data, {
headers: {'Content-type':'multipart/form-data'} }).then(response => {
const {data:filenames} = response; onChange(prev => {
return [...prev, ...filenames]; }); }) }
Dùng 'Content-type':'multipart/form-data' để có thể gửi nhiều file cùng lúc lOMoARcPSD| 36625228 lOMoARcPSD| 36625228
Chương 3. Cài đặt và kiểm thử 3.1. Cài đặt: 3.1.1 Back end:
- Bước 1: Mở terminal và cd đến folder api
- Bước 2: Tạo một .env với nội dung là đường link kết nối với mongoodb
MONGO_URL = mongodb+srv://booking:gvF7bIzlGMh40ptv@cluster0.jhiwsro.mongodb.net/?
retryWrites=true&w=majority
- Bước 3 : Tạo folder uploads trong folder api
- Bước 4: chạy câu lệnh npm install để download node modules- Bước 5:
chạy câu lệnh npm start để khởi động 3.1.2 Front end:
- Bước 1: Mở terminal và cd đến folder client
- Bước 2: Tạo file .evn với nội dung là url của backend
VITE_API_BASE_URL = http://localhost:4000/api lOMoARcPSD| 36625228
- Bước 3: Chạy câu lệnh npm install để download node modules
- Bước 4: Chạy câu lệnh npm run dev để khởi động chương trình
- Bước 5: Truy cập đường dẫn http://localhost:5173/
3.2. Demo chương trình - Trang chủ:
- Đăng ký: Chọn vào avatar phía trên góc phải ở trang chủ để chuyển đến
trang đăng nhập. Sau đó chọn “Register now” để chuyển đến trang đăng ký
và nhập thông tin đăng ký rồi nhấn “Register” lOMoARcPSD| 36625228
Sau khi đăng kí thành công trang sẽ chuyển đến trang đăng nhập
- Đăng nhập: Chọn vào avatar phía trên góc phải ở trang chủ để chuyển đến
trang đăng nhập. Sau đó nhập thông tin đăng nhập và bấm “Login” lOMoARcPSD| 36625228
Sau khi đăng nhập thành công thì sẽ chuyển đến trang chủ lOMoARcPSD| 36625228
- Xem thông tin chi tiết của từng phòng: Ở trang chủ, ta sẽ chọn phòng mà
chúng ta muốn xem. Trang web sẽ chuyển đến trang xem thông tin chi tiết
của phòng đó và hiển thị tất cả thông tin của phòng đó bao gồm hình ảnh,
mô tả, vị trí, giá, …. lOMoARcPSD| 36625228
- Booking: Khi chọn được phòng mong muốn, người dùng sẽ tiến hành nhập
thời gian checkIn, checkOut, số lượng người, tên, số điện thoại và nhấn
“Booking this place” để đặt hàng lOMoARcPSD| 36625228
Sau khi đặt hàng thành công thì trang sẽ chuyển đến trang chi tiết đơn hàng lOMoARcPSD| 36625228
- Hủy đơn đặt hàng: Khi booking thành công thì trang sẽ chuyển đến chi tiết
đơn hàng, nếu muốn hủy đơn thì nhấn chọn “Cancel order” và xác nhận
hủy. Hoặc người dùng có thể chọn vào tên của mình ở phía trên bên phải
trang web và chọn vào “My booking” sau đó chọn đến nơi mà bạn muốn
hủy nhấn chọn vào nơi đó và nhấn “Cancel order” và xác nhận hủy. lOMoARcPSD| 36625228 lOMoARcPSD| 36625228 lOMoARcPSD| 36625228
- Xem và chỉnh sửa các nơi đã đăng: Chọn vào tên người dùng ở phía trên
bên phải trang web và chọn “My accommodation” để xem tất cả các nơi đã đăng lOMoARcPSD| 36625228
Nếu muốn chỉnh thử thông tin của nơi nào thì chọn vào nơi đó trang web sẽ
chuyển đến trang cập nhật và nhập nội dung cần chỉnh sửa vào và nhấn “Save” lOMoARcPSD| 36625228 lOMoARcPSD| 36625228
- Thêm nơi ở mới: Chọn vào “Add new place” ở trong “My
accommodation” sao đó nhập tất cả các thông tin cần thiết và hình ảnh(có
thể tải bằng link hoặc từ thiết bị cá nhân) và nhấn “Save” để đăng nơi ở mới. lOMoARcPSD| 36625228
Nếu upload ảnh bằng link thì sẽ dán link ảnh vào và nhấn “Add photo” lOMoARcPSD| 36625228
Nếu tải ảnh từ thiết bị thì nhấn chọn “Upload” và tải ảnh lên và có thể tải lên nhiều ảnh cung lúc lOMoARcPSD| 36625228
Thêm các thông tin cần thiết và giá phòng vào và nhấn “Save” lOMoARcPSD| 36625228
Nơi ở mới đã được thêm
- Đăng xuất: Chọn “Log out” trong My profile để tiến hành đăng xuất lOMoARcPSD| 36625228 lOMoAR cPSD| 36625228
Chương 4. Kết Luận 41. Kết Luận:
Về cơ bản, em tự nhận xét rằng em đã cơ bản hoàn thành phần yêu cầu đề ra,
và sau đây là một số ưu điểm và nhược điểm rút ra từ web Booking App: - Về ưu điểm:
+ Giao diện dễ sử dụng.
+ Các chức năng được thể hiện rõ ràng.
+ Chương trình nhẹ, hoạt động mượt mà. Về nhược điểm
+ Các chức năng còn cơ bản và chưa thật sự được tối ưu.
+ Thiết kế còn đơn giản.
4.2. Hướng phát triển
- Hoàn thiện các tính năng. - Upload ảnh lên cloud.
- Cải thiện trải nghiệm người dùng.
- Thêm nhiều tính năng mới hơn để hoàn thiện tốt nhất trang web.
- Phát triển giao diện đẹp mắt hơn.