Commit dbb28bff authored by Piyaphorn Arphornsri's avatar Piyaphorn Arphornsri

add

parent 340845da
......@@ -64,7 +64,7 @@ exports.login = async (req, res) => {
return res.status(400).json({ message: "รหัสผ่านไม่ถูกต้อง" });
}
const token = await createToken(user.dataValues.id);
res.status(200).send("success");
res.status(200).send({token});
}
};
......@@ -76,4 +76,5 @@ exports.currentUser = async (req, res) => {
id: decode.id
}
});
res.status(200).send(user);
};
......@@ -21,7 +21,7 @@ exports.getList = async (req, res) => {
try {
let list = await List.findAll();
res.status(200).send("success");
res.status(200).send(list);
} catch (err) {
console.log(err);
res.sendStatus(401);
......@@ -43,15 +43,15 @@ exports.deletelist = async (req, res) => {
}
};
exports.getListId = async (req, res) => {
exports.getListshop = async (req, res) => {
try {
const listId = req.params.id;
const listshop = req.params.shop;
let list = await List.findOne({
where: {
id: listId
shop: listshop
}
});
res.status(200).send("success");
res.status(200).send(list);
} catch (err) {
console.log(err);
res.sendStatus(401);
......
......@@ -7,11 +7,6 @@ module.exports = db.sequelize.define("promotions", {
primaryKey: true,
autoIncrement: true
},
dete: {
type: Sequelize.DATE
},
detail: {
type: Sequelize.STRING
},
......
......@@ -29,6 +29,9 @@ module.exports = db.sequelize.define("shops", {
map: {
type: Sequelize.TEXT
},
facebook: {
type: Sequelize.TEXT
},
type: {
type: Sequelize.ENUM(
"สปาและนวด",
......
......@@ -11,6 +11,7 @@ router.get("/getUserById/:id", authController.getUserById);
router.post("/register", authController.register);
router.post("/login", authController.login);
router.get('/currentuser', authController.currentUser);
module.exports = router;
......@@ -7,7 +7,7 @@ const listController = require("../../controllers/list");
router.post("/add", listController.addlist);
router.get('/all', listController.getList);
router.delete('/delete/:listId',listController.deletelist);
router.get('/getListId/:id',listController.getListId);
router.get('/getListshop/:shop',listController.getListshop);
router.put('/updateList/id',listController.updateList);
......
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
.*.lb
## Intermediate documents:
*.dvi
*.xdv
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
## Generated if empty string is given at "Please type another file name for output:"
.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Build tool directories for auxiliary files
# latexrun
latex.out/
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.pre
*.snm
*.vrb
# changes
*.soc
# comment
*.cut
# cprotect
*.cpt
# elsarticle (documentclass of Elsevier journals)
*.spl
# endnotes
*.ent
# fixme
*.lox
# feynmf/feynmp
*.mf
*.mp
*.t[1-9]
*.t[1-9][0-9]
*.tfm
#(r)(e)ledmac/(r)(e)ledpar
*.end
*.?end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
*.glsdefs
# gnuplottex
*-gnuplottex-*
# gregoriotex
*.gaux
*.gtex
# htlatex
*.4ct
*.4tc
*.idv
*.lg
*.trc
*.xref
# hyperref
*.brf
# knitr
*-concordance.tex
# TODO Comment the next line if you want to keep your tikz graphics files
*.tikz
*-tikzDictionary
# listings
*.lol
# makeidx
*.idx
*.ilg
*.ind
*.ist
# minitoc
*.maf
*.mlf
*.mlt
*.mtc[0-9]*
*.slf[0-9]*
*.slt[0-9]*
*.stc[0-9]*
# minted
_minted*
*.pyg
# morewrites
*.mw
# nomencl
*.nlg
*.nlo
*.nls
# pax
*.pax
# pdfpcnotes
*.pdfpc
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# scrwfile
*.wrt
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# pdfcomment
*.upa
*.upb
# pythontex
*.pytxcode
pythontex-files-*/
# tcolorbox
*.listing
# thmtools
*.loe
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# todonotes
*.tdo
# vhistory
*.hst
*.ver
# easy-todo
*.lod
# xcolor
*.xcp
# xmpincl
*.xmpi
# xindy
*.xdy
# xypic precompiled matrices
*.xyc
# endfloat
*.ttt
*.fff
# Latexian
TSWLatexianTemp*
## Editors:
# WinEdt
*.bak
*.sav
# Texpad
.texpadtmp
# LyX
*.lyx~
# Kile
*.backup
# KBibTeX
*~[0-9]*
# auto folder when using emacs and auctex
./auto/*
*.el
# expex forward references with \gathertags
*-tags.tex
# standalone packages
*.sta
\chapter{บทนำ}
\section{ที่มาและเหตุผล }
เนื่องจากปัจจุบัน การดำเนินธุรกิจร้านเสริมสวยที่ให้บริการเสริมความงาม เช่น การทำผม ตัดผม ออกแบบทรงผม อบไอน้ำ เวลาผู้ใช้บริการมาใช้บริการโดยจะมาที่ร้านเลยโดยที่ไม่จองคิวพบว่าร้านที่มาใช้บริการมีลูกค้าเป็นจำนวนมาก อาจจะทำให้ต้องรอคิวนานหรือต้องเสียเวลามาใช้บริการในวันอื่นบางครั้งผู้ใช้บริการมีเบอร์ของร้านเสริมสวยก็จะโทรมา สอบถามคิวและจองคิว แต่ช่างติดทำผมให้ลูกค้าท่านอื่นก็ไม่สามารถรับโทรศัพท์ได้ผู้พัฒนาจึงมีแนวคิดว่าจะทำระบบการจอง คิวร้านเสริมสวยขึ้น เพื่อแก้ปัญหาการรอคิวนานและให้มีความทันสมัยตลอดจนสามารถรองรับการแสดงผลบนอุปกรณ์ สมาร์ทโฟนในปัจจุบัน ทำให้สามารถจองคิวหรือติดต่อสื่อสารในเรื่องของการจองคิวทำผมกับทางร้านได้สะดวกมากยิ่งขึ้น
แนวทางการแก้ปัญหา จัดทำการพัฒนาเป็นเว็บแอปพิเคชัน ระบบจองคิวร้านเสริมสวยที่ถูกพัฒนาขึ้นเป็นเว็บแอปพิเคชัน จะช่วยเพิ่มระเบียบในการจัดการจองคิวให้เป็นระบบ ลดขั้นตอนการดำเนินงานที่ซับซ้อน ลดระยะเวลาในการดำเนินงาน ลดความผิดพลาดที่จะเกิดขึ้นในขั้นตอนการดำเนินงาน และช่วยเพิ่มประสิทธิภาพในการทำงาน
\section{วัตถุประสงค์}
\begin{enumerate}
\item เพื่อออกแบบและพัฒนาเว็บแอปพลิเคชั่น จองคิวร้านเสริมสวย
\item เพื่อแก้ปัญหาการรอคิวร้านเสริมสวย
\end{enumerate}
\section{ขอบเขตของโครงงาน}
\begin{enumerate}[label=1.3.\arabic*]
\item เจ้าของร้าน
\begin{itemize}
\item สามารถลงทะเบียนเข้าสู่ระบบด้วย Email ได้
\item สามารถดูคิวที่ผู้ใช้บริการได้ทำการจองคิวไว้
\item สามารถจัดการคิวได้
\item สามารถ post ภาพผลงานทั้งหมดของร้านได้
\item สามารถเพิ่ม แก้ไข และลบรายการให้บริการประจำร้านได้
\item สามารถเพิ่ม แก้ไข และลบ ข้อมูลร้านได้
\item สามารถเพิ่ม แก้ไข และลบข้อมูลตำแหน่งร้านได้
\end{itemize}
\item ช่างประจำร้าน
\begin{itemize}
\item ลงทะเบียนใช้ web ด้วย Email ได้
\item สามารถดูตารางการทำงานของตนเองได้
\item สามารถ post ภาพผลงานของตัวเองได้
\item สามารถแก้ไขข้อมูลส่วนตัวได้
\end{itemize}
\item ผู้ใช้บริการ
\begin{itemize}
\item สามารถลงทะเบียนเข้าสู่ระบบด้วย Email ได้
\item สามารถค้นหาร้านเสริมสวยได้
\item สามารถจองคิวของร้านเสริมสวยได้
\item สามารถดูคิวว่างของร้านเสริมสวยได้
\item สามารถดูข้อมูลต่างๆของร้านเสริมสวยได้
\item สามารถดูตำแหน่งของทางร้านได้
\item สามารถดูผลงานของร้านได้
\item สามารถเขียนรีวิว ติชม ได้
\end{itemize}
\end{enumerate}
\newpage
\section{ประโยชน์ที่คาดว่าจะได้รับ}
\begin{enumerate}
\item ช่วยลดขั้นตอนการจองคิวร้านเสริมสวย
\item ช่วยลดปัญหาในการใช้บริการที่เกิดจากการรอคิวนาน
\item ช่วยให้การจองคิวมีระเบียบมากขึ้น
\end{enumerate}
\section{เครื่องมือที่ใช้ในการพัฒนา (Development tools)}
\subsection{ฮาร์ดเเวร์}
\begin{enumerate}
\item เครื่องคอมพิวเตอร์ส่วนบุคคล (Personal computer) เพื่อใช้ในการพัฒนาโมบายแอปพลิเคชัน โดยมีคุณสมบัติอย่างน้อยดังนี้
\begin{itemize}
\item ทำงานบนระบบปฏิบัติการ Elementary OS พื้นฐานการทำงานบน Windows 10
\item หน่วยประมวลผลกลาง AMD Rezen(™) 5 3500U
\item หน่วยประมวลผลกราฟฟิก AMD Radeon(™) Vega(8) Mobile Graphics
\item หน่วยความจำหลักอย่างน้อย 8 กิกะไบต์ (Gigabyte, GB)
\item หน่วยความจำสำรองอย่างน้อย 256 กิกะไบต์ (Gigabyte, GB)
\end{itemize}
\end{enumerate}
\subsection{ซอฟต์แวร์ (Software)}
\begin{enumerate}
\item React JS เป็น Java Script Framework ที่ใช้ในการพัฒนาเว็บไซต์ฝั่งผู้ใช้งาน
\item Node Js เป็น เทคโนโลยีที่ใช้ในการพัฒนาเว็บไซต์ฝั่งที่ติดต่อกับฐานข้อมูล
\item JavaScript เป็น ภาษาที่ใช้ในการพัฒนาเว็บ
\item MySQL เป็น โปรแกรมระบบจัดการฐานข้อมูล
\item Visual Studio Code เป็น Code Editor ที่ใช้ในการเเก้ไขเเละปรับเเต่ง Code
\item Google Map API เทคโนโลยีที่ใช้ในการใช้งานแผนที่
\end{enumerate}
\newpage
\section{แผนการดำเนินการ}
ในการสร้างระบบแนะนำสถานที่ท่องเที่ยวในจังหวัดอุบลราชธานี ผู้พัฒนาได้แบ่งขั้นตอนการดำเนินงานไว้ด้วยกัน 8 ขั้นตอน ดังตารางที่ \ref{tab:ganttchart}
%\begin{landscape}
%\sffamily
\begin{table}[H]
\noindent
\caption{ขั้นตอนการดำเนินงาน}
\begin{ganttchart}[
canvas/.append style={fill=none, draw=black!5, line width=.75pt},
vgrid={*2{draw=black!7, line width=.75pt}},
title label font=\bfseries\footnotesize,
bar label node/.append style={
align=left,
text width=width("7. Functional Testing On")},
bar/.append style={draw=none, fill=black!63}
]{1}{18}
\gantttitle{2562}{10}
\gantttitle{2563}{8}\\
\gantttitle{ส.ค.}{2}
\gantttitle{ก.ย.}{2}
\gantttitle{ต.ค.}{2}
\gantttitle{พ.ย.}{2}
\gantttitle{ธ.ค.}{2}
\gantttitle{ม.ค.}{2}
\gantttitle{ก.พ.}{2}
\gantttitle{มี.ค.}{2}
\gantttitle{เม.ย.}{2} \\
\ganttbar{1.ศึกษาความเป็นไปได้}{1}{2} \\
\ganttbar{2.เสนอหัวข้อโครงงาน}{2}{2} \\
\ganttbar{3.ศึกษาค้นคว้าข้อมูล}{3}{8} \\
\ganttbar{4.ศึกษาการใช้เครื่องมือ}{3}{4} \\
\ganttbar{5.วิเคราะห์และออกแบบ}{5}{15}\\
\ganttbar{6.เขียนโปรแกรม}{6}{17} \\
\ganttbar{7.ทดสอบและแก้ปัญหา}{6}{17} \\
\ganttbar{8.จัดทำเอกสาร}{11}{17} \\
\end{ganttchart}
\label{tab:ganttchart}
\end{table}
%\end{landscape}
%TODO แก้เทมเพลตเอาชื่อตารางไว้ด้านบน
\chapter{ทฤษฎีที่เกี่ยวข้อง}
ในบทนี้จะกล่าวถึงรายละเอียดเกี่ยวกับทฤษฎีและงานวิจัยที่เกี่ยวของกับการพัฒนาระบบจองคิวร้านเสริมสวย
โดยแบ่งเนื้อหาออกเป็น 2 ส่วน ได้แก่ ส่วนที่หนึ่งเป็นเนื้อหาพื้นฐานเกี่ยวกับทฤษฎีการเขียนโปรแกรมและเทคโนโลยีที่นำมาใช้ในการพัฒนาในหัวข้อที่ 2.1 - 2.6
ได้แก่ ความรู้พื้นฐานเกี่ยวกับ React Node.js JavaScript MySQL Visual studio code Google maps API
และในส่วนที่สองเป็นเนื้อหาเกี่ยวกับเว็ปแอพพลิเคชันที่เกี่ยวข้องกับโครงงานนี้เว็บแอพพลิเคชัน Gowabi
\section{ความรู้พื้นฐานเกี่ยวกับ React}
React เป็น JavaScript Library ที่ถูกสร้างโดย Facebook ซึ่ง React ทำหน้าที่เป็นเพียง User Interface (UI) ที่สร้างมาจากพื้นฐานแนวความคิดแบบ Model View Controller (MVC)
React ทำหน้าที่เฉพาะส่วน View (จาก Model View Controller) เหมาะกับงาน Web Front-End ที่สามารถแบ่งออกเป็น Web Component ย่อยๆ โดยหลักการวิเคราะห์ควรแยกให้ย่อยที่สุดเท่าที่จะทำได้ ซึ่งสามารถแบ่ง Component ออกเป็น 2 รูปแบบ คือ
\begin{itemize}
\item Container สำหรับบรรจุ Component หลักย่อยอื่นๆ ซึ่งไม่ควรมีการเก็บค่าใดๆ (สามารถทำหน้าที่เป็นตัวกลางในการส่งผ่านค่าได้) เน้นไปที่การจัด Layout
\item Web Component คือ ส่วนที่ต้อง interact กับผู้ใช้จริงๆ เช่น ช่องกรอกข้อมูล ลิสต์แสดงข้อมูล ลาเบล (Label) และ ปุ่ม เป็นต้น ซึ่งอาจมีการเก็บค่าบางค่าเอาไว้ที่ สเตท (State) เพื่อนำมาแสดงผล
\end{itemize}
การเขียน React จำเป็นต้องมีความรู้ใน 3 ประเด็น ได้แก่
\begin{itemize}
\item Component – ส่วนประกอบต่างๆ ในเว็บ จะถูกมองเป็น Component
\item State – ข้อมูลที่อยู่ใน Component แต่ละชิ้น เรียกว่า State
\item Props – ข้อมูลที่ถูกส่งต่อจาก Component ชั้นบนลงไปชั้นล่าง ซึ่งเรียกว่า Props (Properties)
\end{itemize}
อธิบายประเด็นหลักทั้ง 3 ประเด็น ดังรูปที่ \ref{Fig:react-concept}
\begin{figure}[H]
\centering
\includegraphics[scale=0.6]{Figures/2/react-concept}
\caption{concept หลักของ React}{ที่มา: https://www.designil.com/wp-content/uploads/2017/07/react-concept-designil.jpg}
\label{Fig:react-concept}
\end{figure}
\subsection{React Life Cycle}
การเขียน render() ฟังก์ชันใน component \cite{react3} นั้น ควรจะเขียนในแบบ pure function ซึ่งจะไม่มีการเปลี่ยน state และสร้าง side effect ต่อภายนอกทั้งสิ้น อย่างเช่น การ call extenal service แบบ Ajax request, Firebase calling เป็นต้น เพราะหน้าที่ของ render() มีแค่การ render UI เท่านั้น หากไม่สามารถทำสิ่งดังกล่าวภายใน render() แล้ว กิจกรรมเหล่านั้นจึงสามารถทำได้ที่ life cycle ของ React
ตลอดช่วงวงจรชีวิต \cite{react4} สามารถควบคุมเหตุการณ์ต่างๆ ที่เกิดขึ้นในการแสดงผล UI การอัพเดทข้อมูล และการ re-rendering จนกระทั้งข้อมูลนั้นหายไป โดยที่ React ได้มีการเตรียมฟังก์ชันต่างๆ ไว้ สามารถอธิบายการทำงานของฟังก์ชันได้ ดังนี้
\begin{itemize}
\item componentWillMount() : คุณสมบัติของ componentWillMount ไม่มีอะไรเกี่ยวกับการใช้งาน component เพราะยังไม่มีการ mount อะไรขึ้นมา โดยมีหน้าที่ คือ การกำหนดค่าเริ่มต้นสำหรับการใช้งาน
\item componentDidMount() : เกิดขึ้นเมื่อทำการ Mount เรียบร้อย พร้อมที่จะใช้งาน โดยปกติจะใช้ในการกำหนดค่าทุกอย่างที่ต้องใช้ DOM และรับข้อมูลที่ต้องการมาแสดงผล
\item componentWillReceiveProps(nextProps) : เมื่อ Component ทำงาน จนกระทั้งมี pro-ps ใหม่เข้ามา เพื่อทำการเปลี่ยนแปลงข้อมูล componentWillReceive Props จะถูกเรียก โดยมี nextProps เป็นตัวแปรที่ถูกส่งเข้ามา
\item shouldComponentUpdate(nextProps, nextState) : ถูกเรียกเมื่อ component มีการเปลี่ยนแปลงด้วย nextProps กับ nextState
\item componentWillUpdate(nextProps, nextState) : ถูกเรียกก่อนที่จะ render หลังจากได้รับค่าใหม่ของ props หรือ state คุณสมบัติของคล้ายกับ componentWillReveiveProps
\item componentDidUpdate(prevProps, prevState) : ถูกเรียกทันที่หลังจากเกิดการเปลี่ยนแปลงของ component แต่จะไม่ถูกเรียกตอนครั้งแรกที่ render โดยที่ componentDidUpd-ate สามารถใช้งานได้เหมือน componentDidMount
\item componentWillUnmount() : ถูกเรียกก่อนที่ component ทำการ unmount และ destroy โดยปกติแล้วจะใช้เพื่อทำการรีเซ็ต (reset) ค่าต่างๆ
\end{itemize}
\subsection{React Life Cycle}
การเขียน render() ฟังก์ชันใน component \cite{bib5} นั้น ควรจะเขียนในแบบ pure function ซึ่งจะไม่มีการเปลี่ยน state และสร้าง side effect ต่อภายนอกทั้งสิ้น อย่างเช่น การ call extenal service แบบ Ajax request, Firebase calling เป็นต้น เพราะหน้าที่ของ render() มีแค่การ render UI เท่านั้น หากไม่สามารถทำสิ่งดังกล่าวภายใน render() แล้ว กิจกรรมเหล่านั้นจึงสามารถทำได้ที่ life cycle ของ React
ตลอดช่วงวงจรชีวิต \cite{bib6} สามารถควบคุมเหตุการณ์ต่างๆ ที่เกิดขึ้นในการแสดงผล UI การอัพเดทข้อมูล และการ re-rendering จนกระทั้งข้อมูลนั้นหายไป โดยที่ React ได้มีการเตรียมฟังก์ชันต่างๆ ไว้ สามารถอธิบายการทำงานของฟังก์ชันได้ ดังนี้
\begin{itemize}
\item componentWillMount() : คุณสมบัติของ componentWillMount ไม่มีอะไรเกี่ยวกับการใช้งาน component เพราะยังไม่มีการ mount อะไรขึ้นมา โดยมีหน้าที่ คือ การกำหนดค่าเริ่มต้นสำหรับการใช้งาน
\item componentDidMount() : เกิดขึ้นเมื่อทำการ Mount เรียบร้อย พร้อมที่จะใช้งาน โดยปกติจะใช้ในการกำหนดค่าทุกอย่างที่ต้องใช้ DOM และรับข้อมูลที่ต้องการมาแสดงผล
\item componentWillReceiveProps(nextProps) : เมื่อ Component ทำงาน จนกระทั้งมี pro-ps ใหม่เข้ามา เพื่อทำการเปลี่ยนแปลงข้อมูล componentWillReceive Props จะถูกเรียก โดยมี nextProps เป็นตัวแปรที่ถูกส่งเข้ามา
\item shouldComponentUpdate(nextProps, nextState) : ถูกเรียกเมื่อ component มีการเปลี่ยนแปลงด้วย nextProps กับ nextState
\item componentWillUpdate(nextProps, nextState) : ถูกเรียกก่อนที่จะ render หลังจากได้รับค่าใหม่ของ props หรือ state คุณสมบัติของคล้ายกับ componentWillReveiveProps
\item componentDidUpdate(prevProps, prevState) : ถูกเรียกทันที่หลังจากเกิดการเปลี่ยนแปลงของ component แต่จะไม่ถูกเรียกตอนครั้งแรกที่ render โดยที่ componentDidUpd-ate สามารถใช้งานได้เหมือน componentDidMount
\item componentWillUnmount() : ถูกเรียกก่อนที่ component ทำการ unmount และ destroy โดยปกติแล้วจะใช้เพื่อทำการรีเซ็ต (reset) ค่าต่างๆ
\end{itemize}
\section{ความรู้พื้นฐานเกี่ยวกับ Node.js}
Node.js \cite{nodejs} เป็นภาษาที่ทำงานอยู่ในฝั่งเซิร์ฟเวอร์ (server) ซึ่ง syntax ที่ใช้ในการเขียนคือ JavaScript และเป็นภาษาที่ออกแบบมาให้ทำงานแบบ Event-Driven หรือทำงานเมื่อเกิดเหตุการณ์ตามที่กำหนดไว้ และการทำงานแบบ Asynchronous ซึ่งสามารถทำงานในลำดับต่อไปโดยที่ไม่ต้องรอให้งานก่อนหน้าเสร็จก่อนแล้วจึงทำงานขั้นต่อไป แต่ก็สามารถกำหนดให้ทำงานแบบ Synchronous ได้เช่นกัน โดยการกำหนด Callback เมื่องานแรกทำงานเสร็จแล้ว นอกจากนี้ Node.js นั้นจะใช้ Compiler จาก Google JavaScript Engine V8
ส่วนใหญ่จะนิยมใช้ node.js ในงานที่ทำเป็นเบื้องหลัง คือ งานที่ประมวลผลสั่งเซิร์ฟเวอร์ซึ่งเป็นงานที่อาจจะต้อง interface กับผู้ใช้ หรือไม่ต้อง interface กับผู้ใช้ ตัวอย่างงานที่ต้อง interface กับผู้ใช้ เช่น การทำตัวเองเป็น http server ในการดึงหน้าเว็บมาแสดงผลให้กับ user หรือว่า การเปิด socket เพื่อรับส่งข้อมูลกันระหว่างเซิร์ฟเวอร์กับผู้ใช้งาน เช่น ทำเป็นห้อง chat ทำเกม ทำระบบที่ป้อนข้อมูลเพื่อคำนวณผลลัพธ์ เป็นต้น ตัวอย่างงานที่ไม่ต้อง interface กับผู้ใช้ เช่น ทำ spider crawler เว็บ คือ การเปิดเว็บแล้วเก็บข้อมูลไปเรื่อยๆ หรือ โปรแกรมที่ รอรับค่าจาก streaming ต่างๆ เพื่อนำมาบันทึกไว้ ซึ่งการทำงานเหล่านี้ไม่จำเป็นต้อง interface กับผู้ใช้
node.js มีส่วนเสริมที่ชื่อว่า node package management (npm) ซึ่งเปรียบเหมือน google play ใน android หรือ app store ใน iOS ที่สามารถเอา package ที่คนอื่นเขียนเอาไว้แล้ว เพื่อแจกฟรี (free) มาต่อยอดเพื่อใช้ในงานของตนได้ โดยตัวอย่างที่ได้รับความนิยมจะเป็น underscore, async, request และ express เป็นต้น สำหรับการติดตั้ง ใช้คำสั่ง npm install ตามด้วยชื่อ package ที่ต้องการติดตั้ง \cite{nodejs1}
node.js มีการทำงานเป็น Asynchronous คือ การทำงานบางอย่างไม่ต้องรอให้บรรทัดนั้นทำงานเสร็จ เช่น ส่งคำสั่งไป query ข้อมูลจากฐานข้อมูล แล้วสามารถข้ามไปทำงานบรรทัดต่อไปโดยไม่ต้องรอผลจากฐานข้อมูล เมื่อการทำงานนั้นทำงานเสร็จจึงค่อยรอผลลัพธ์กลับมา ดังนั้นปัญหาจะเกิดทันที ถ้าการทำงานต่อไปนำผลลัพธ์จากคำสั่งก่อนหน้านั้นมาใช้ต่อ ซึ่งส่งผลให้เกิดการทำงานผิดพลาด เพราะผลลัพธ์ยังไม่ได้รับกลับมา
\subsection{node.js ทำงานแบบ event driven}
การทำงานของ node \cite{nodejs2} เรียกว่าเป็นการขับเคลื่อนด้วย event ต่างๆ ที่เกิดขึ้น ทำให้สามารถข้ามจาก event หนึ่งที่เสร็จแล้วไปยัง event อื่นได้ด้วยการสั่งงานต่อเนื่องกันไป หรือการสั่งให้ event หลาย event เริ่มทำงานในเวลาใกล้เคียงกัน ประโยชน์ที่ได้จาก event driven คือ การสั่งให้รอรับ event นั้นไปตลอดการณ์ โดยไม่เปลืองทรัพยากร เช่น การเชื่อมต่อไปยัง streaming channel ที่หนึ่ง ซึ่งอาจเป็น text หรือข้อมูลบางอย่าง เช่น ปริมาณน้ำฝน เอาไว้ หากต้นทางของ streaming ยังไม่มีข้อมูลส่งมา จะไม่เกิด event ใดๆ และ node.js จะรออยู่ แต่หากต้นทาง streaming มีข้อมูลมา node.js จะทำงานเพื่อตอบสนองต่อ event ที่เกิดขึ้นนั้นทันที สามารถแสดงการทำงานดังกล่าวได้ ดังรูปที่ \ref{Fig:event-driven}
\begin{figure}[H]
\centering
\includegraphics[scale=0.5]{Figures/2/event-driven}
\caption{การทำงานแบบ event driven}{ที่มา: http://meewebfree.com/u/i/nodejs/\texttt{node\_js\_stage}.png}
\label{Fig:event-driven}
\end{figure}
จากรูปที่ \ref{Fig:event-driven} สามารถอธิบายการทำงานได้ดังนี้ การทำงานของ stage1 เมื่อเรียกใช้การทำงานของ stage2 แล้ว ไม่จำเป็นต้องรอให้ stage2 ทำงานเสร็จก่อน ซึ่ง stage1 สามารถเรียกการทำงานของ stage3 ได้เลยโดยไม่จำเป็นต้องรอการทำงานของ stage2
\subsection{ข้อดีของ Node.js}
\begin{itemize}
\item มีการทำงานแบบ Event-Driven และ Asynchronous
\item เหมาะกับการทำ Web แบบ Real time
\item ประหยัดทรัพยากร ในการทำงาน
\item มีการประมวลผลที่รวดเร็ว
\end{itemize}
\section{ความรู้พื้นฐานเกี่ยวกับ JavaScript}
JavaScript \cite{javascript} คือ ภาษาคอมพิวเตอร์สำหรับการเขียนโปรแกรมบนระบบอินเทอร์เน็ต (Internet) ที่กำลังได้รับความนิยมอย่างสูง JavaScript เป็น ภาษาสคริปต์ (script) เชิงวัตถุ ที่เรียกกันว่า "สคริปต์" ซึ่งการใช้ JavaScript ในการสร้างและพัฒนาเว็บไซต์ (ใช้ร่วมกับ HTML) จะช่วยให้เว็บไซต์ดูมีการเคลื่อนไหว สามารถตอบสนองผู้ใช้งานได้มากขึ้น โดยมีวิธีการทำงานในลักษณะ "แปลความและดำเนินงานไปทีละคำสั่ง" (interpret) หรือเรียกว่า โปรแกรมเชิงวัตถุ (Object Oriented Programming) ที่มีเป้าหมายในการ ออกแบบและพัฒนาโปรแกรมในระบบอินเทอร์เน็ต สำหรับผู้เขียนด้วยภาษา HTML สามารถทำงานข้ามแพลตฟอร์มได้ โดยทำงานร่วมกับ ภาษา HTML และภาษา Java ได้ทั้งทางฝั่งไคลเอนต์ (client) และ ทางฝั่งเซิร์ฟเวอร์
JavaScript ถูกพัฒนาขึ้นโดย บริษัท เน็ตสเคปคอมมิวนิเคชันส์ (Netscape Communications Corporation) โดยใช้ชื่อว่า Live Script ออกมาพร้อมกับ Netscape Navigator 2.0 เพื่อใช้สร้างเว็บเพจ (Web page) โดยติดต่อกับเซิร์ฟเวอร์แบบ Live Wire ต่อมาเน็ตสเคปได้ร่วมมือกับ บริษัทซันไมโครซิสเต็มส์ (Sun Microsystems, Inc) ปรับปรุงระบบของเบราว์เซอร์ (Browser) เพื่อให้สามารถติดต่อใช้งานกับภาษาจาวา (Java) ได้ และได้ปรับปรุง LiveScript ใหม่เมื่อ ปี พ.ศ. 2538 แล้วตั้งชื่อใหม่ว่า JavaScript ซึ่ง JavaScript ทำให้การสร้างเว็บเพจมีลูกเล่นต่างๆ มากมาย และยังสามารถโต้ตอบกับผู้ใช้ได้อย่างทันที เช่น การใช้เมาส์คลิก หรือ การกรอกข้อความในฟอร์ม เป็นต้น
เนื่องจาก JavaScript ช่วยให้ผู้พัฒนา สามารถสร้างเว็บเพจได้ตรงกับความต้องการ และมีความน่าสนใจมากขึ้น ประกอบกับเป็นภาษาเปิด ที่ทุกคนสามารถนำไปใช้ได้ ดังนั้นจึงได้รับความนิยมเป็นอย่างสูง มีการใช้งานอย่างกว้างขวาง รวมทั้งได้ถูกกำหนดให้เป็นมาตรฐานโดย European Computer Manufacturer's Association (ECMA) การทำงานของ JavaScript จะต้องมีการแปลความคำสั่ง ซึ่งขั้นตอนนี้จะถูกจัดการโดยเบราว์เซอร์ (เรียกว่าเป็น client-side script) ดังนั้น JavaScript จึงสามารถทำงานได้เฉพาะบนเบราว์เซอร์ที่สนับสนุน ซึ่งปัจจุบันเบราว์เซอร์เกือบทั้งหมดสามารถสนับสนุน JavaScript แล้ว อย่างไรก็ดี สิ่งที่ต้องระวังคือ JavaScript มีการพัฒนาเป็นเวอร์ชันใหม่ๆ ออกมาด้วย ดังนั้น ถ้านำโค้ด (code) ของเวอร์ชันใหม่ ไปรันบนเบราว์เซอร์รุ่นเก่าที่ยังไม่สนับสนุน อาจจะทำให้เกิด error ได้
\subsection{ประโยชน์ของ JavaScript}
\begin{itemize}
\item JavaScript ทำให้สามารถใช้เขียนโปรแกรมแบบง่ายได้ โดยไม่ต้องพึ่งภาษาอื่น
\item JavaScript มีคำสั่งที่ตอบสนองกับผู้ใช้งาน เช่น เมื่อผู้ใช้คลิกที่ปุ่ม หรือ Checkbox สามารถสั่งให้เปิดหน้าใหม่ได้ ทำให้เว็บไซต์มีปฏิสัมพันธ์กับผู้ใช้งานมากขึ้น
\item JavaScript สามารถเขียนหรือเปลี่ยนแปลง HTML Element ได้ นั่นคือสามารถเปลี่ยนแปลงรูปแบบการแสดงผลของเว็บไซต์ได้ หรือหน้าแสดงเนื้อหาสามารถซ่อนหรือแสดงเนื้อหาได้โดยง่าย
\item JavaScript สามารถใช้ตรวจสอบข้อมูลได้ เช่น เมื่อผู้ใช้งานกรอกข้อมูลบางเว็บไซต์ เช่น Email เมื่อบันทึกข้อมูลผิดจะมีหน้าต่างฟ้องขึ้นมาว่ากรอกผิด หรือลืมบันทึกอะไรบางอย่าง เป็นต้น
\item JavaScript สามารถใช้ในการตรวจสอบผู้ใช้ได้เช่น ตรวจสอบว่าผู้ใช้ ใช้ web browser อะไร
\item JavaScript สร้าง Cookies (เก็บข้อมูลของผู้ใช้ในคอมพิวเตอร์ของผู้ใช้เอง) ได้
\end{itemize}
\subsection{ข้อดีและข้อเสียของ Java JavaScript}
JavaScript \cite{javascript1} ทำงานบนเว็บบราวเซอร์ (client-side script) จึงไม่มีข้อจำกัดว่าจะใช้เซิร์ฟเวอร์แบบไหนก็ตาม เพราะ JavaScript ทำงานเฉพาะในเครื่องของผู้ใช้งานเท่านั้น ซึ่งต่างกับภาษาสคริปต์อื่น เช่น PHP , ASP, JSP หรือ Perl ซึ่งต้องประมวลผลและทำงานที่เครื่องเซิร์ฟเวอร์ (server-side script) จึงจำเป็นต้องใช้บนเซิร์ฟเวอร์ ที่สนับสนุนภาษาเหล่านี้เท่านั้นจึงจะสามารถใช้งาน server-side script ได้ แต่อย่างไรก็ตาม จากลักษณะการทำงานที่กล่าวมาก็ทำให้ JavaScript มีข้อจำกัด กล่าวคือคือไม่สามารถรับและส่งข้อมูลต่างๆ กับเซิร์ฟเวอร์โดยตรง เช่น การอ่านไฟล์จากเซิร์ฟเวอร์ เพื่อนำมาแสดงบนเว็บเพจ หรือรับข้อมูลจากผู้ชม เพื่อนำไปเก็บบนเซิร์ฟเวอร์ เป็นต้น ดังนั้นงานลักษณะนี้ จึงยังคงต้องอาศัยภาษา server-side script อยู่ (ความจริงมี JavaScript ที่ทำงานบนเซิร์ฟเวอร์เช่นกัน ซึ่งต้องอาศัยเซิร์ฟเวอร์ที่สนับสนุนโดยเฉพาะเช่นกัน แต่ไม่เป็นที่นิยมนัก)
นักพัฒนาเว็บส่วนใหญ่จึงนิยมใช้ JavaScript ร่วมกับ ภาษา Server Script เพื่อทำการส่งข้อมูลระหว่าง เซิร์ฟเวอร์กับเครื่องของผู้ใช้งาน ซึ่งทำให้การแสดงผลของหน้าเว็บมีความสวยงามและราบรื่นมากยิ่งขึ้น
\section{ความรู้พื้นฐานเกี่ยวกับ MySQl}
MySQL เป็นระบบจัดการฐานข้อมูลเชิงสัมพันธ์ (Relational Database Management System) โดยใช้ภาษา SQL และเป็นซอฟต์แวร์โอเพนซอร์ส
\subsection{ข้อดีของMySQL}
\begin{itemize}
\item เป็นซ็อฟแวร์ฟรี สามารถใช้งานได้โดยไม่ต้องเสียค่าลิขสิทธิ์
\item มีความเร็วในการทำงานสูง
\item มีเสถึยรภาพสูง
\item ทำงานได้กับหลายระบบปฏิบัติการ เช่น UNIX,Linux,Windows 2000,Windows XP
\item ติดตั้งและใช้งานง่ายมีคู่มือให้ดาวน์โหลดได้ฟรี
\item เหมาะกับธุรกิจขนาดเล็กและขนาดกลาง
\end{itemize}
เนื่องจากภาษา SQl เป็นภาษาที่ใช้ในการเขียนโปรแกรม เพื่อจัดการกับฐานข้อมูลโดยเฉพาะ ซึ่งในระบบงานจะใช้ 4 คำสั่งหลักๆคือ
\begin{itemize}
\item SELECT ใช้สำหรับดึงข้อมูลที่ต้องการ
\item UPDATE ใช้สำหรับแก้ไขข้อมูล
\item INSERT ใช้สำหรับการเพิ่มข้อมูล
\item DELETE ใช้สำหรับลบข้อมูล
\end{itemize}
\end{itemize}
\section{ความรู้เกี่ยวกับ Visual Studio Code}
วิชวล สตูดิโอโค้ด (Visual Studio Code หรือ VSCode) \cite{vscode} เป็นโปรแกรม Code Editor ที่ใช้ในการแก้ไขและปรับแต่งโค้คถูกพัฒนาโดยค่ายไมโครซอฟท์(Microsoft) มีการพัฒนาออกมาในรูปแบบของ OpenSource
จึงสามารถนำมาใช้งานได้แบบไม่ต้องเสียค่าใช้จ่าย Visual Studio Code เหมาะสำหรับนักพัฒนาโปรแกรมที่ต้องการใช้งานข้ามแพลตฟอร์ม (Cross-platform) โดยจะรองรับการใช้งานทั้งบนระบบปฏิบัติการ Windows,macOS และ Linux
ซึ่งภาษาที่ Visual Studio Code รองรับการทำงานซึ่งมีมากกว่า 30 ภาษาโปรแกรม เช่น C++, C, CSS, Dockerfile, HTML, JavaScript, JSON, Less, Markdown, PHP, Python, Sass, TypeScript,
Node.js และ Java เป็นต้น
\section{ความรู้พื้นฐานเกี่ยวกับ Google Maps API}
Google Maps API \cite{maps} เป็นชุด API ของ Google สำหรับพัฒนา web application และ mobile application (Android,ios) มีไว้สำหรับเรียกใช้แผนที่และชุด service ต่างๆ มากมายให้เรียกใช้ เช่น
\begin{itemize}[label={--}]
\item ชุดควบคุมแผนที่ (Map Control)
\item การปรับแต่งแผนที่ (Styled Map)
\item การนำทางจากจุดหนึ่งไปยังอีกจุดหนึ่ง (Directions Service)
\item Street view
\item การดึงข้อมูล POI (Point of Interest) คือข้อมูลสถานที่ต่างๆ ที่ Google รวบรวมไว้ เช่น โรงแรม ห้างสรรพสินค้า โรงเรียน สถานที่ราชการ เป็นต้น เพื่อให้ผุ้ใช้งานนำมาใช้งานได้
\item การแปลงที่อยู่เป็นพิกัด Latitude และ Longitude (Geocoding Service)
\item แผนที่รองรับระบบ 3 มิติ ผู้ใช้งาน สามารถปรับมุมมองเป็นลักษณะ 3 มิติได้ ซึ่งระบบนี้มีให้บริการบางพื้นที่
\end{itemize}
การใช้งาน Google Maps API สามารถขอใช้งานโดยใช้บัญชี Gmail และเข้าไปที่เว็บ \\ https://cloud.google.com/maps-platform/ ดังรูปที่ \ref{Fig:2-maps}
\begin{figure}[H]
\centering
\includegraphics[width=14cm]{Figures/2/maps}
\caption{หน้าเว็บบริการของ Google Maps}{ที่มา : https://cloud.google.com/maps-platform/}
\label{Fig:2-maps}
\end{figure}
\newpage
\section{เอกสารและงานวิจัยที่เกี่ยวข้อง}
\subsection{เว็บแอพพลิเคชัน Gowabi}
Wongnai\cite{wongnai} เป็นเว็บไซต์และแอปพลิเคชันที่ให้บริการเกี่ยวกับการค้นหาร้านอาหาร สถานที่ท่องเที่ยว ร้านเสริมสวย มีฟังก์ชันการทำงานพื้นฐานอันได้แก่ การค้นหา ดูข้อมูล โพสต์รีวิว บันทึกร้านที่ชอบ เป็นต้น
\subsection{ข้อแตกต่างระหว่างเว็บแอปพลิเคชั่น wongnai กับเว็บของโครงงาน}
เว็บแอปพลิเคชั่นของ wognai ยังไม่มีฟังก์ชันการแนะนำสถานที่ที่เหมาะสมแก่ผู้ใช้ ผู้พัฒนาจึงได้ทำฟังก์ชันการแนะนำสถานที่ที่เหมาะสมตามรสนิยมของผู้ใช้เพิ่มลงในเว็บของโครงงาน
\begin{figure}[H]
\centering
\includegraphics[width=14cm]{Figures/2/wognai}
\caption{หน้าแรกของเว็บไซต์ Wongnai}{ที่มา : https://www.wongnai.com/}
\label{Fig:2-wognai}
\end{figure}
This source diff could not be displayed because it is too large. You can view the blob instead.
\newcommand{\thcodedouble}[1] {\color{red}{\textquotedbl{#1}\textquotedbl}}
\newcommand{\thcodesingle}[1] {\color{red}{\textquotesingle{#1}\textquotesingle}}
\newcommand{\thcodesingleleft}[1] {\color{red}{\textquotesingle{#1}}}
\newcommand{\thcodesingleright}[1] {\color{red}{{#1}\textquotesingle}}
\chapter{การพัฒนาระบบ}
ในบทนี้จะกล่าวถึงการสร้างระบบงานของระบบแนะนำสถานที่ท่องเที่ยวในจังหวัดอุบลราชธานี โดยนำผลที่ได้จากการวิเคราะห์และออกแบบระบบมาสร้างเป็นระบบงานซึ่งจะอธิบายถึงตัวอย่างการเขียน โปรแกรมการทำงานของระบบในส่วนต่างๆดังต่อไปนี้
\section{การพัฒนาในส่วนสมัครสมาชิก}
เมื่อผู้ใช้กรอกข้อมูลการสมัครเสร็จแล้วทำการกดปุ่มสมัครสมาชิก ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-Register} - \ref{Fig:4-Register1}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript} //เปลี่ยนเป็นภาษาที่เขียน
saveRegister() {
if (this.typeGender == "Male") {
this.selectGender = |\thcodedouble{ชาย}|;
}
else if (this.typeGender == "Female") {
this.selectGender = |\thcodedouble{หญิง}|;
}
this.data = {
content: {
profileIds: ["Ubontrip"],
bookmarks: "[]",
reviews: "[]",
places: "[]",
username: this.username,
fulluser: this.fulluser,
gender: this.selectGender,
imageSrc: this.imageSrc
},
credentials: {
local: {
username: this.username,
password: this.password
}
}
}
}
\end{minted}
\caption{การทำงานของระบบเมื่อกดปุ่มสมัครสมาชิก}
\label{Fig:4-Register}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-Register} โครงสร้างของไฟล์ register.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 ฟังก์ชัน saveRegister() เป็นฟังก์ชันที่ใช้ควบคุมการทำงานในการสมัครสมาชิก
\item บรรทัดที่ 2 - 7 เป็นการกำหนดค่าข้อมูลเพศเมื่อผู้ใช้กรอกเข้ามาจะเช็คดูว่า value ที่ผู้ใช้เลือกเป็น Male หรือ Female
\item บรรทัดที่ 8 เก็บข้อมูลไว้ที่ data โดยรูปแบบที่เก็บจะต้องตรงกับรูปแบบที่อยู่ในฐานข้อมูล
\item บรรทัดที่ 9 content จะใช้เก็บข้อมูลทั่วไปที่เราต้องการจะบันทึก
\item บรรทัดที่ 10 เป็นการกำหนด roles ให้กับผู้ใช้
\item บรรทัดที่ 11 - 17 เป็นข้อมูลพื้นฐานที่จะใช้เก็บประวัติของผู้ใช้ bookmarks เก็บข้อมูลการบันทึกสถานที่ reviews เก็บข้อมูลการเขียนรีวิว places เก็บข้อมูลการเพิ่มสถานที่
\item บรรทัดที่ 19 - 24 เป็นการกำหนดให้ใช้ข้อมูล 2 อย่างนี้ในการเข้าสู่ระบบ คือ username และ password
\end{itemize}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript} //เปลี่ยนเป็นภาษาที่เขียน
if (this.password === this.confirm) {
this.http.post('${KUZZLE}:${KPORT}/users/${this.username}/_create',
this.data)
alert (|\thcodedouble{ลงทะเบียนสำเร็จ}|);
this.router.navigate(['/popular}']);
}else{
alert (|\thcodedouble{รหัสผ่านและยืนยันรหัสผ่านไม่ตรงกันกรุณากรอกรหัสผ่านใหม่อีกครั้ง}|);
}
\end{minted}
\caption{การทำงานของระบบเมื่อกดปุ่มสมัครสมาชิก (ต่อ)}
\label{Fig:4-Register1}
\end{figure}
จากภาพที่ \ref{Fig:4-Register1} โครงสร้างของไฟล์ register.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 - 3 เป็นการเช็คการกรอกข้อมูลรหัสผ่านและรหัสยืนยันว่าตรงกันไหมถ้าตรงกันระบบจะทำการสร้าง user เข้าไปในระบบฐานข้อมูล
\newpage
\item บรรทัดที่ 4 - 5 เมื่อสร้าง user สำเร็จจะแสดงข้อมความแจ้งผู้ใช้ว่า "ลงทะเบียนสำเร็จ" และทำการ router ไปยังหน้า popular
\item บรรทัดที่ 6 - 8 เป็นการเช็คเงื่อนไงในกรณีที่รหัสผ่านไม่ตรงกัน จะแสดงข้อความแจ้งผู้ใช้ว่า "รหัสผ่านและยืนยันรหัสผ่านไม่ตรงกัน กรุณากรอกรหัสผ่านใหม่อีกครั้ง"
\end{itemize}
\section{การพัฒนาในส่วนการเข้าสู่ระบบ}
เมื่อผู้ใช้กรอกข้อมูลทั้งหมดเสร็จทำการกดปุ่มเข้าสู่ระบบ ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-login}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
Login(): void {
this.http.post(`${KUZZLE}:${KPORT}/_login/local?expiresIn=1y`,{
username: this.username,
password: this.password
})
.subscribe((res: any) => {
localStorage.setItem('JWT', res.result.jwt);
this.http.get(`${KUZZLE}:${KPORT}/users/_me`, {
headers: {
Authorization: "Bearer" + localStorage.getItem('JWT')
}
}).subscribe((_res: any) => {
if (_res.status === 200) {
localStorage.setItem("UserInfo", JSON.stringify(_res.result._source))
this.loginService.isAuth({
isAuth: true,
data: _res.result
})
this.dialogRef.close();
}
})
})
}
\end{minted}
\caption{การทำงานของระบบเมื่อกดเข้าสู่ระบบ}
\label{Fig:4-login}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-login} โครงสร้างของไฟล์ dialoglogin.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1เมื่อผู้ใช้กดเข้าสู่ระบบจะเข้ามาทำงานในฟังก์ชัน Login()
\item บรรทัดที่ 2- 5 เป็นการยิงข้อมูล username และ password ที่ผู้ใช้กรอกเข้ามาเพื่อนำไปเช็คกับข้อมูลที่อยู่ในฐานข้อมูล
\item บรรทัดที่ 7 เป็นการสร้างตัวแปร JWT ขึ้นมาเก็บค่าของ token ไว้ใน localStorage
\item บรรทัดที่ 10 เป็นการอ่านค่าตัวแปร JWT
\item บรรทัดที่ 13 เป็นการเช็คเงื่อนไขสเตตัสในการเข้าสู่ระบบ
\item บรรทัดที่ 14 ถ้าสเตตัสที่ล็อคอินเข้าสู่ระบบเป็น 200 จะสร้าง UserInfo ขึ้นมาเก็บค่าข้อมูลไว้ใน localStorage
\item บรรทัดที่ 15 - 18 เรียกใช้ service isAuth เพื่อเก็บค่าข้อมูลผู้ใช้ โดยข้อมูลจะถูกเก็บไว้ในตัวแปร data
\item บรรทัดที่ 19 เป็นการปิดหน้าการเข้าสู่ระบบลง
\end{itemize}
\newpage
\section{การพัฒนาในส่วนการจัดอันดับสถานที่}
เมื่อผู้ใช้กดเลือกหมวดเพื่อดูการจัดอับดับ ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-pupular} - \ref{Fig:4-pupular1}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
selectGroup(event: any) {
this.select = event.value;
switch (this.select) {
case "travel":
this.results_populartravel = []
const list_allTravel = [
{
type : "travelwaterfall",
review : "review_waterfall"
},{
type : "travelwaterpark",
review : "review_waterpark"
},{
type : "traveltemple",
review : "review_temple"
},{
type : "travelnational",
review : "review_national"
},{
type : "travelshop",
review : "review_shop"
},{
type : "travelbar",
review : "review_bar"
}]
list_allTravel.map(travels => {
const query = {
query: {
}, size: 100
}
this.http.post(`${KUZZLE}:${KPORT}/${KINDEX}/${travels.type}/_search`,query)
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดเลือกหมวด}
\label{Fig:4-pupular}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-pupular} โครงสร้างของไฟล์ popular.component.ts อธิบายการทำงานได้ดังนี้
โดยผู้พัฒนาจะของยกตัวอย่างการทำงานแค่ของหมวดสถานที่ท่องเที่ยว(case:travel) เนื่องจากว่าทั้ง 3 case มีการทำงานเหมือนกันได้แก่ case: "travel" case:"eat" case: "hotel"
\begin{itemize}[label={--}]
\item บรรทัดที่ 1เมื่อผู้ใช้กดเลือกหมวดสถานที่จะเข้ามาทำงานในฟังก์ชัน selectGroup()โดยรับค่าพารามิเตอร์มา1ตัว
\item บรรทัดที่ 2 เป็นการเช็ดค่าที่ผู้ใช้กดเลือกมาเก็บไว้ในตัวแปร select
\item บรรทัดที่ 3 เป็นการเขียน switch case เช็คค่าที่ผู้ใช้เลือก โดยจะมีด้วยกันทั้งหมด 3 case ได้แก case:"travel" , case:"eat", case:"hotel"
\item บรรทัดที่ 4 ถ้าผู้ใช้เลือกหมวดท่องเที่ยวก็จะเข้ามาทำงานใน case:"travel" แต่ถ้าผู้ใช่ไม่ได้เลือกหมวดท่องเที่ยวก็จะไปทำงานใน case อื่นที่ไม่ใช้ travel
\item บรรทัดที่ 6 - 25 เป็นการประกาศค่าเพื่อเก็บข้อมูลในรูปแบบ Object โดย type เก็บประเภทของสถานที่ และ review เก็บประเภทของรีวิว
\item บรรทัดที่ 26 นำข้อมูลที่ถูกเก็บไว้ใน listallTravel มาทำการ map เพื่อดูข้อมูลทั้งหมด
\item บรรทัดที่ 27 - 30 เป็นการกำหนดจำนวนของข้อมูลที่ต้องการค้นหาออกมาแสดง
\item บรรทัดที่ 31 เป็นการ query หาข้อมูลในฐานข้อมูลตามประเภทสถานที่ที่ผู้ใช้เลือก โดยใช้รูปแบบ method POST ในการค้นหา
\end{itemize}
\newpage
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
.subscribe((_res: any) => {
_res.result.hits.map(_travel => {
let total_place = 0; let avg = 0;
this.http.get(`${KUZZLE}:${KPORT}/${KINDEX}/${travels.review}/
${_travel._id}`)
.subscribe((res: any) => {
res.result._source.review.map(_item => {
total_place += _item.score
})
avg = total_place / res.result._source.review.length
this.results_populartravel.push({
..._travel, avg,
review: travels.review
})
this.results_populartravel.sort(
(a, b) => (a.avg > b.avg) ? 1 : ((b.avg > a.avg) ? -1 : 0)
).reverse()
}, err => {
this.results_populartravel.push({
..._travel, avg: 0,
review: travels.review
})
this.results_populartravel.sort(
(a, b) => (a.avg > b.avg) ? 1 : ((b.avg > a.avg) ? -1 : 0)
).reverse()
})
})
})
break;
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดเลือกหมวด (ต่อ)}
\label{Fig:4-pupular1}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-pupular1} โครงสร้างของไฟล์ popular.component.ts อธิบายการทำงานได้ดังนี้(ต่อ)
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 เป็นการ subscribe เพื่อดูข้อมูลข้างในที่ได้จากการ query ข้อมูล
\item บรรทัดที่ 2 เป็นการจิ้มข้อมูลเข้าไปชั้นข้างในเพื่อให้ได้ข้อมูลที่ต้องการและทำการวนฟอดูข้อมูลข้างใน
\item บรรทัดที่ 3 เป็นการประกาศตัวแปรtotalPlace ใช้เก็บค่าผลรวม ส่วน avg ใช้เก็บค่าเฉลี่ยของการให้คะแนนสถานที่
\item บรรทัดที่ 4 - 7 เป็นการ ค้นหาข้อมูลจากฐานข้อมูล เมื่อได้ข้อมูลมาจะใช้ .subscribe ในการเข้าดูข้อมูลโดยจะประกาศ res ขึ้นมาเก็บค่าข้อมูลที่ได้ทำการค้นหาและทำการจิ้มข้อมูลใน res เข้าไปถึงส่วนของข้อมูลที่เราต้องการ
\item บรรทัดที่ 8 เป็นการนำค่าที่ได้ในการวนฟอในแต่ละรอบมาเก็บไว้ที่ totalplace
\item บรรทัดที่ 10 เป็นการคำนวณคะแนนเฉลี่ยในการรีวิวของแต่ละสถานที่และค่าที่จะนำมาเก็บไว้ในตัวแปร avg
\item บรรทัดที่ 11 - 13 เป็นการ push ค่าที่ได้ไปเก็บไว้ที่ resultspopulartravel
\item บรรทัดที่ 15 - 17 เป็นการนำค่าที่ถูกเก็บไว้มาทำการจัดอันดับโดยจะเช็คว่า ค่าเฉลี่ยของตัวที่ 1 มีค่ามากกว่าตัวที่ 2 ถ้าใช่ก็ให้มีค่าเป็น 1 ถ้าค่าเฉลี่ยตัวที่ 2 มากกว่าตัวที่ 1 ก็ให้มีค่าเป็น -1 และทำการ reverse() ข้อมูลโดยจะเช็คไปแบบนี้จนครบถึงจะหยุดทำงาน
\item บรรทัดที่ 18 - 21 ถ้าข้อมูลสถานที่นั้นยังไม่มีรีวิว ก็จะทำการ push ข้อมูลสถานที่โดยจะกำหนดให้ค่าของ avg = 0
\item บรรทัดที่ 23 - 25 เป็นการนำค่าที่ถูกเก็บไว้มาทำการจัดอันดับโดยจะเช็คว่า ค่าเฉลี่ยของตัวที่ 1 มีค่ามากกว่าตัวที่ 2 ถ้าใช่ก็ให้มีค่าเป็น 1 ถ้าค่าเฉลี่ยตัวที่ 2 มากกว่าตัวที่ 1 ก็ให้มีค่าเป็น -1 และทำการ reverse() ข้อมูลโดยจะเช็คไปแบบนี้จนครบถึงจะหยุดทำงาน
\end{itemize}
\newpage
\section{การพัฒนาในส่วนของเขียนรีวิว}
เมื่อผู้ใช้กดปุ่มบันทึกรีวิว ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-review} - \ref{Fig:4-review2}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
onClick(rating: number): void {
this.rating = rating;
}
reviewPlace() {
const now = moment(moment().valueOf()).format("DD/MM/YYYY H:mm")
if (this.id_review === undefined) {
this.dataReview = {
review: [
{
id: this.id_place,
create_by: JSON.parse(localStorage.getItem("UserInfo")).username,
imageUser: JSON.parse(localStorage.getItem("UserInfo")).imageSrc,
title: this.title,
description: this.description,
image: this.imageReview,
score: this.rating,
timeReview: now
}]}
this.http.post(`${KUZZLE}:${KPORT}/${KINDEX}/${this.idReview}/
${this.id_place}/_create`,this.dataReview)
.subscribe((response) => {
})
this.reviews = [...this.reviews, {
id: this.id_place,
type: this.type_place,
review: this.idReview
}]
this.http.put(`${KUZZLE}:${KPORT}/users/${this.id_user}/_update`, {
reviews: JSON.stringify(this.reviews)
})
}
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มเขียนรีวิว}
\label{Fig:4-review}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-review} โครงสร้างของไฟล์ dialog-review.component.ts อธิบายการทำงานได้ดังนี้(ต่อ)
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 - 3 ฟังก์ชัน onClick() เป็นฟังก์ชันของการให้คะแนนสถานที่ โดยจะเก็บคะแนนที่ผู้ใช้ให้ไว้ในต rating
\item บรรทัดที่ 4 ฟังก์ชัน reviewPlace() จะทำงานเมื่อผู้ใช้กดปุ่ม บันทุกรีวิว โดยจะทำการบันทึกข้อมูลรีวิว
\item บรรทัดที่ 5 เป็นการประกาศ now ขึ้นมาเก็บค่าของวันที่ในการเขียนรีวิว โดยจะมี format วันที่เป็น วัน/เดือน/ปี
\item บรรทัดที่ 6 เป็นเงื่อนไขในการเช็คว่า id ของรีวิวสถานที่นี้ว่า มีค่าเท่ากับ undefined หรือไม่ถ้าเท่าจะเข้ามาทำงานในเงื่อนไขนี้
\item บรรทัดที่ 7 - 8 เป็นการประกาศตัวแปรชื่อ dataReview มาเก็บข้อมูลการเขียนรีวิวโดยใน dataReview จะมีการเก็บเป็นอาเรย์ในตัวแปร review
\item บรรทัดที่ 10 เป็นการเก็บ id ของสถานที่รีวิวไว้ในตัวแปรชื่อ id
\item บรรทัดที่ 11 เป็นการเก็บชื่อผู้ใช้ที่เขียนรีวิวโดยจะไปอ่านค่ามาจาก localStoragemใน filed ชื่อ username และเก็บไว้ในตัวแปรชื่อ createby
\item บรรทัดที่ 12 เป็นการเก็บรูปภาพของผู้ใช้ที่เขียนรีวิวโดยจะไปอ่านค่ามาจาก localStoragemใน filed ชื่อ imageSrc และเก็บไว้ในตัวแปรชื่อ imageUser
\item บรรทัดที่ 13 เป็นการเก็บหัวข้อของรีวิวไว้ในตัวแปรชื่อ title
\item บรรทัดที่ 14 เป็นการเก็บรายละเอียดของการเขียนรีวิวไว้ในตัวแปรชื่อ description
\item บรรทัดที่ 15 เป็นการเก็บรูปภาพรีวิวไว้ในตัวแปรชื่อ image
\item บรรทัดที่ 16 เป็นการเก็บค่าของคะแนนรีวิวไว้ในตัวแปร score
\item บรรทัดที่ 17 เป็นการเก็บเวลาของงการเขียนรีวิวไว้ในตัวแปรชื่อ timeReview
\item บรรทัดที่ 19 - 21 เป็นการทำงานโดยจะเอาข้อมูลที่ได้จากการเขียนรีวิวในตัวแปรชื่อ dataReview ไปทำการสร้าง document เพื่อเก็บข้อมูลรีวิวของสถานที่นั้นโดยใช้ method post ในการสร้าง หลังจากสร้างเสร็จจะ log ค่าออกมาดูเพื่อดูผลลัพธ์
\newpage
\item บรรทัดที่ 23 - 27 เป็นการประกาศตัวแปรชื่อ reviews มาเก็บประวัติของผู้ใช้ว่าเคยเขียนรีวิวสถานที่ไหนบ้าง โดยจะเก็บ id คือเก็บชื่อสถานที่ type คือประเภทสถานที่และ review คือประเภทของสถานที่ที่เขียนรีวิว
\item บรรทัดที่ 28 - 29 เป็นการนำข้อมูลที่อยู่ในก้อน reviews ไปทำการอัปเดทข้อมูลผู้ใช้ในส่วนของการเขียนรีวิว
\end{itemize}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
else {
this.dataReview = {
review: [
...this.result_ReviewPlace,
{
id : this.id_place,
create_by: JSON.parse(localStorage.getItem("UserInfo")).username,
imageUser: JSON.parse(localStorage.getItem("UserInfo")).imageSrc,
title: this.title,
description: this.description,
image: this.imageReview,
score: this.rating,
timeReview: now
}
]
}
this.http.put(`${KUZZLE}:${KPORT}/${KINDEX}/${this.idReview}/
${this.id_place}/_update`,this.dataReview)
.subscribe((response) => {
console.log(response);
}, err => {
console.log({ ...err });
})
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มเขียนรีวิว(ต่อ)}
\label{Fig:4-review1}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-review1} โครงสร้างของไฟล์ dialog-review.component.ts อธิบายการทำงานได้ดังนี้(ต่อ)
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 เป็นเงื่อนไขเช็คว่าถ้า id ของสถานที่ไม่เท่ากับ undefined ก็จะเข้ามาทำงานในส่วนนี้
\item บรรทัดที่ 2 - 3 เป็นการประกาศตัวแปรชื่อ dataReview มาเก็บข้อมูลการเขียนรีวิวโดยใน dataReview จะมีการเก็บเป็นอาเรย์ในตัวแปร review
\item บรรทัดที่ 4 เป็นนำค่ารีวิวเดิมเดิมที่มีอยู่ มาต่อกับรีวิวตัวใหม่
\item บรรทัดที่ 6 เป็นการเก็บ id ของสถานที่รีวิวไว้ในตัวแปรชื่อ id
\item บรรทัดที่ 7 เป็นการเก็บชื่อผู้ใช้ที่เขียนรีวิวโดยจะไปอ่านค่ามาจาก localStoragemใน filed ชื่อ username และเก็บไว้ในตัวแปรชื่อ createby
\item บรรทัดที่ 8 เป็นการเก็บรูปภาพของผู้ใช้ที่เขียนรีวิวโดยจะไปอ่านค่ามาจาก localStoragemใน filed ชื่อ imageSrc และเก็บไว้ในตัวแปรชื่อ imageUser
\item บรรทัดที่ 9 เป็นการเก็บหัวข้อของรีวิวไว้ในตัวแปรชื่อ title
\item บรรทัดที่ 10 เป็นการเก็บรายละเอียดของการเขียนรีวิวไว้ในตัวแปรชื่อ description
\item บรรทัดที่ 11 เป็นการเก็บรูปภาพรีวิวไว้ในตัวแปรชื่อ image
\item บรรทัดที่ 12 เป็นการเก็บค่าของคะแนนรีวิวไว้ในตัวแปร score
\item บรรทัดที่ 13 เป็นการเก็บเวลาของงการเขียนรีวิวไว้ในตัวแปรชื่อ timeReview
\item บรรทัดที่ 17 - 19 เป็นการทำงานโดยเอาข้อมูลที่ได้จาก dataReview ไปทำการอัปเดทข้อมูลการเขียนรีวิวของสถานที่นั้้นในฐานจข้อมูลโดยทำผ่าน method put และเมื่อทำสำเร็จจะแสดงข้อความบอกว่า เพิ่มคอมเม้นสำเร็จ ใน log
\item บรรทัดที่ 21 - 22 ถ้าทำงานล้มเหลวจะให้แสดง error
\end{itemize}
\newpage
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
const check = this.reviews.find(item => item.id == this.id_place)
if (check) {
this.reviews.map((item, index) => {
item.id === this.id_place ? this.reviews.splice(index, 1) : null
})
this.reviews = [...this.reviews, {
id: this.id_place,
type: this.type_place,
review: this.idReview
}
]
this.http.put(`${KUZZLE}:${KPORT}/users/${this.id_user}/_update`,{
reviews: JSON.stringify(this.reviews)
})
.subscribe(res => {
console.log(res);
}, err => {
console.log(err);
})
}
onNoClick(): void {
this.dialogRef.close();
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มเขียนรีวิว(ต่อ)}
\label{Fig:4-review2}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-review2} โครงสร้างของไฟล์ dialog-review.component.ts อธิบายการทำงานได้ดังนี้(ต่อ)
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 เป็นการประกาศ check ขึ้นมาเพื่อค้นหาข้อมูลการเขียนรีวิวทั้งหมดที่มีอยู่โดยใช้ item เป็นรับข้อมูลเข้าไปค้าหาต่อ
\item บรรทัดที่ 2 - 3 เป็นการเช็คเงื่อนไขว่าถ้าเช็ค เมื่อได้ข้อมูล reviews ก็จะทำการวนฟอเพื่อดูข้อมูล
\item บรรทัดที่ 4 - 5 เมื่อมันผ่านเงื่อนไขก็จะเข้ามาเช็คดูอีดครั้งว่าค่าของ id มีค่าเท่ากับ idplaceห ไหม ถ้าตรงกันก็จะให้รีวิวที่มีอยู่ออกไปแต่ถ้าไม่ใช้ก็จะให้ค่าตัวนั้นมี่ค่าเป็น null
\item บรรทัดที่ 6 - 11 เป็นการประกาศตัวแปรชื่อ review ขึ้นมาเพื่อเก็บประวัติข้อมูลการเขียนรีวิวของผูัใช้
\item บรรทัดที่ 12 - 14 นำข้อมูลที่ได้จาก reviews ไปทำการอัพเดทข้อมูลของผูัใช้
\item บรรทัดที่ 15 - 19 เป็นการ log ดูค่าเมื่ออัพเดทสำเร็จก็ และเมื่ออัปเดทไม่สำเร็จจะแสดง error
\item บรรทัดที่ 21 - 23 ฟังก์ชัน onNoClick เมื่อผู้ใช่ไม่ต้องทีจะเขียนเรีวิว ระบบก็จะปิดหน้าเขียนรีวิวลง
\end{itemize}
\newpage
\section{การพัฒนาในส่วนของการเพิ่มสถานที่}
เมื่อผู้ใช้กดปุ่มเพิ่มสถานที่ ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-addplace} - \ref{Fig:4-addplace2}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
onFileSelected(e) {
var file = e.dataTransfer ? e.dataTransfer.files[0] : e.target.files[0];
var pattern = /image-*/;
var reader = new FileReader();
if (!file.type.match(pattern)) {
alert('invalid format');
return;
}
reader.onload = this._handleReaderLoaded.bind(this);
reader.readAsDataURL(file);
}
_handleReaderLoaded(e) {
let reader = e.target;
this.imageSrc = reader.result;
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มเพิ่มสถานที่}
\label{Fig:4-addplace}
\end{figure}
จากภาพที่ \ref{Fig:4-addplace} โครงสร้างของไฟล์ dialog-add.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 - 15 เป็นฟังก์ชันการการอัพโหลดรูปภาพแนวนำรูปภาพที่อัพโหลดมาแปลงเป็น base64 เพื่อที่จะไปเก็บไว้ใในฐานข้อมูล
\end{itemize}
\newpage
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
addPlace() {
var selectplace = ''
if(this.typeplace == "hotelvilla"){
selectplace = |\thcodedouble{วิลล่า}|; }
else if(this.typeplace == "hotelhostel"){
selectplace = |\thcodedouble{โรงแรม}|;}
else if(this.typeplace == "hotelresort"){
selectplace = |\thcodedouble{รีสอร์ท}|;}
else if(this.typeplace == "hoteltent"){
selectplace = |\thcodedouble{กางเต้นท์}|;}
else if(this.typeplace == "hotelgateshouse"){
selectplace = |\thcodedouble{บังกะโล/เกสท์เฮาส์}|;}
else if(this.typeplace == "hotelhomestay"){
selectplace = |\thcodedouble{โฮมสเตย์}|;}
else if(this.typeplace == "travelwaterfall"){
selectplace = |\thcodedouble{น้ำตก}|;}
else if(this.typeplace == "travelwaterpark"){
selectplace = |\thcodedouble{สวนน้ำ/สวนสัตว์}|;}
else if(this.typeplace == "traveltemple"){
selectplace = |\thcodedouble{สถานที่ทางศาสนา}|;}
else if(this.typeplace == "travelnational"){
selectplace = |\thcodedouble{อุทยานและธรรมชาติ}|;}
else if(this.typeplace == "travelshop"){
selectplace = |\thcodedouble{ถนนคนเดิน}|;}
else if(this.typeplace == "travelbar"){
selectplace = |\thcodedouble{สถานบันเทิง}|;}
else if(this.typeplace == "eatgrill"){
selectplace = |\thcodedouble{บุฟเฟ่ต์/ปิ้งย่าง}|;}
else if(this.typeplace == "eatdrinks"){
selectplace = |\thcodedouble{คาเฟ่}|;}
else if(this.typeplace == "eatnoodle"){
selectplace = |\thcodedouble{เส้น}|;}
else if(this.typeplace == "eatesan"){
selectplace = |\thcodedouble{อาหารท้องถิ่น}|;}
else if(this.typeplace == "eatjapan"){
selectplace = |\thcodedouble{อาหารญี่ปุ่น}|;}
else if(this.typeplace == "eatsteak"){
selectplace = |\thcodedouble{สเต็ก}|;}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มเพิ่มสถานที่(ต่อ)}
\label{Fig:4-addplace1}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-addplace1} โครงสร้างของไฟล์ dialog-add.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 ฟังก์ชัน addPlace() เป็นฟังก์ชันที่ใช้ทำการเพิ่มข้อมูลสถานที่จะทำงานก็ต่อเมื่อผู้ใช้กดปุ่มบันทึก
\item บรรทัดที่ 2 เป็นการประกาศตัวแปร selectplace ที่มี type เป็น string เพื่อที่จะใช้เก็บค่าประเภทของสถานที่ที่ผู้ใช้เลือก
\item บรรทัดที่ 3 - 38 เป็นการเช็คเงื่อนไขเมื่อผู้ใช้เลือกประเภทของสถานที่เพื่อที่จะกำหนดค่าให้กับ selectplace
\end{itemize}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
this.data = {
"image": this.imageSrc,
"namelocation": this.namelocation,
"group": selectplace,
"address": this.address,
"cityprovince": this.cityprovince,
"lat": this.lat,
"lng": this.lng,
"openclose": this.openclose,
"phonenumber": this.phonenumber,
"price": this.price,
"description": this.description,
"facebook": this.facebook,
"website": this.website
};
this.http.post(`${KUZZLE}:${KPORT}/${KINDEX}/${this.typeplace}/_create`,
this.data)
.subscribe((res:any) =>{
const idname =res.result._id
this.places = [...this.places,{
id: idname,
type: this.typeplace,
review: this._typeReview
}]
this.http.put(`${KUZZLE}:${KPORT}/users/${this.id_user}/_update`,{
places: JSON.stringify(this.places)
})
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มเพิ่มสถานที่(ต่อ)}
\label{Fig:4-addplace2}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-addplace2} โครงสร้างของไฟล์ dialog-add.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 เป็นการประกาศตัวแปรชื่อ data โดยมี type เป็น object เพื่อที่จะมาเก็บค่าของรายละเอียดข้อมูลสถานที่ที่ผู้ใช้กรอกเข้ามา
\item บรรทัดที่ 2 เป็นการประกาศ image ขึ้นมาเก็บรูปภาพของสถานที่โดยมีรูปแบการเก็บเป็น base64
\item บรรทัดที่ 3 เป็นการประกาศ namelocation ขึ้นมาเพื่อที่จะเก็บชื่อสถานที่
\item บรรทัดที่ 4 เป็นการเก็บประเภทของสถานที่ที่ได้จากการเลือกของผู้ใช้ไว้ใน group
\item บรรทัดที่ 5 เป็นการเก็บที่อยู่ของสถานที่ที่ได้จากการกรอกเข้ามาจากผู้ใช้ไว้ใน address
\item บรรทัดที่ 6 เป็นการเก็บชื่ออำเภอและจังหวัดที่ได้จากการข้อมูลเข้ามาจากผู้ใชเไว้ใน cityprovince
\item บรรทัดที่ 7 เป็นการเก็บค่าละติจูดที่ได้จากการกรอกข้อมูลเข้ามาจากผู้ใช้ไว้ใน lat
\item บรรทัดที่ 8 เป็นการเก็บค่าลองจิจูดที่ได้จากการกรอกข้อมูลเข้ามาจากผู้ใช้ไว้ใน lng
\item บรรทัดที่ 9 เป็นการเก็บข้อมูลเวลาเปิด ปิด ของสถานที่ที่ได้จากการกรอกเข้ามาจากผู้ใช้ไว้ใน openclose
\item บรรทัดที่ 10 เป็นการเก็บข้อมูลเบอร์โทรของสถานที่ที่ได้จากการกรอกเข้ามาจากผู้ใช้ไว้ใน phonenumber
\item บรรทัดที่ 11 เป็นการเก็บข้อมูลราคาของอาหาร ราคาเข้าชมสถานที่ ที่ได้จากกการกรอกเข้ามาจากผู้ใช้ไว้ใน price
\item บรรทัดที่ 12 เป็นการเก็บข้อมูลรายละเอียดของสถานที่ที่ได้จากการกรอกเข้ามาจากผู้ใช้ไว้ใน description
\item บรรทัดที่ 13 เป็นการเก็บชื่อ facebook ของสถานที่ทีได้จากการกรอกเข้ามาจากผู้ใช้ไว้ใน facebook
\item บรรทัดที่ 14 เป็นการเก็บชื่อ website ของสถานที่ที่ได้จากกการกรอกเข้ามาจากผู้ใช้ไว้ใน website
\item บรรทัดที่ 16 เป็น การนำข้อมูลสถานที่ที่ได้จาก data ไปทำการเพิ่มข้อมูลลงในฐานข้อมูลตามเป็นเภทที่ผู้ใช้เลือก
\newpage
\item บรรทัดที่ 18 เป็นการประกาศตัวแปรชื่อ idname เพื่อมาเก็บค่าของ id สถานที่
\item บรรทัดที่ 20 เป็นการเรียกใช้ places เพื่อที่จะนำมาเก็บค่าข้อมูล
\item บรรทัดที่ 21 เป็นการเก็บชื่อ id สถานที่ไว้ใน id
\item บรรทัดที่ 22 เป็นการเก็บประเภทของสถานที่ไว้ใน type
\item บรรทัดที่ 23 เป็นการเก็บประเภทของรีวิวสถานที่ไว้ใน review
\item บรรทัดที่ 25 - 27 เป็นการนำข้อมูลที่ได้จาก places มาทำการอัพเดทข้อมูลในส่วนของผู้ใช้เพื่อที่จะเก็บประวัติการเพิ่มสถานที่ของผู้ใช้ไว้และนำข้อมูลที่ได้ไปแสดงในหน้าโปรไฟล์ผู้ใช้
\end{itemize}
\section{การพัฒนาในส่วนของการลบรีวิว}
เมื่อผู้ใช้กดปุ่มลบรีวิว ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-deletere}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
deleteReview() {
this.http.delete(`${KUZZLE}:${KPORT}/${KINDEX}/${this.type_review}/
${this.id_place}`)
.subscribe((response) =>{
});
const check = this.reviews.find(item => item.id == this.id_place)
if (check) {
this.reviews.map((item, index) => {
if( item.id === this.id_place ){
this.reviews.splice(index, 1)
this.http.put(`${KUZZLE}:${KPORT}/users/${this.id_user}/_update`,{
reviews: JSON.stringify(this.reviews)})
.subscribe(res => {
}, err => {
console.log(err);
})
}
})
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มลบรีวิว}
\label{Fig:4-deletere}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-addplace} โครงสร้างของไฟล์ delete-review.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 ฟังก์ชัน deleteReview()g เป็นฟังก์ชันที่ใช้ในการลบรีวิวจะถูกเรียกใช้เมื่อผู้ใช้กดปุ่มลบรีวิว
\item บรรทัดที่ 2 - 5 เป็นการลบข้อมูลรีวิวออกจากฐานข้อมูลโดยจะลบตามประเภทของสถานที่และ id ของสถานที่ที่ โดยใช้ method delete
\item บรรทัดที่ 6 เป็นการประกาศ check ขึ้นมาเพื่อค้นหาข้อมูลการเขียนรีวิวทั้งหมดที่มีอยู่ในฐานข้อมูลของผู้ใช้โดยใช้ item เป็นรับข้อมูลเข้าไปค้าหาต่อ
\item บรรทัดที่ 7 - 10 เป็นการเช็คเงื่อนไขเมื่อเข้าเงื่อนไขก็จะเข้ามาทำงานในส่วนนี้โดยใช้ข้อมูลที่เก็บไว้ใน reviews ทำการวนฟอเพื่อดูข้อมูล และก็เข้าเงื่อนไขก็จะเช็คดูอีกครั้งว่าค่าของ id มีค่าเท่ากับ idplaceห ไหม ถ้าตรงกันก็จะให้รีวิวลบรีวิวที่มีชื่อ id ตรงกับตัวที่ค้นหาออก
\item บรรทัดที่ 11 - 15 เป็นการอัพเดทข้อมูลในส่วนของผู้ใช้อีกครั้ง โดยใช้ method put เพื่อที่จะทำการอัพเดทข้อมูลการเขียนรีวิว
\item บรรทัดที่ 16 - 18 เป็นการ log ดูค่า error ในกรณีที่อัพเดทข้อมูลไม่สำเร็จ
\end{itemize}
\newpage
\section{การพัฒนาในส่วนของการบันทึกสถานที่}
เมื่อผู้ใช้กดปุ่มบันทึก ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-bookmark}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
changeBookMark(place: any) {
if (this.loginData.isAuth === false) {
this.modalService.info({
nzTitle: 'กรุณาล็อคอินเข้าสู่ระบบก่อนกดปุ่มบันทึกสถานที่'
});
}else {
this.dataplace = {
id: place._id,
type: place._type,
type_review: this.type_Review
}
const check = this.bookmarks.find(item => item.id == place._id)
if (check) {
this.bookmarks.map((item, index) => {
item.id === place._id ? this.bookmarks.splice(index, 1) : null
})
}else {
this.bookmarks.push({
id: place._id,
type: place._type,
review: this.type_Review
})
}
this.http.put(`${KUZZLE}:${KPORT}/users/${this.id_User}/_update`,{
bookmarks: JSON.stringify(this.bookmarks)
})
.subscribe(res => {
console.log(res);
this.bookmark = !this.bookmark
})
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มบันทึกสถานที่}
\label{Fig:4-bookmark}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-addplace} โครงสร้างของไฟล์ detail-place.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 ฟังก์ชัน changeBookMark() ฟังก์ชันนี้จะถูกเรียกใช้ก็ต่อเมื่อผู้ใช้กดปุ่มบันทึก
\item บรรทัดที่ 2 - 5 เป็นการเช็คดูสถานะการล็อคอิน ถ้าสถานะการเข้าสู่ระบบเป็น false จะมีข้อความแจ้งผู้ใช้ให้เข้าสู่ระบบก่อนกดปุ่มบันทึกสถานที่
\item บรรทัดที่ 6 - 11 เป็นการเช็คดูสถานะการล็อคอิน ในกรณีเข้าสู่ระบบสำเร็จแล้วผู้ใช้สมารถกดปุ่มบันทึกสถานที่ได้ โดยจะเก็บค่าของ id สถานที่ type สถานที่ type ของรีวิว ไว้ในตัวแปรชื่อ dataplace
\item บรรทัดที่ 12 เป็นการประกาศ check ขึ้นมาเพื่อเก็บค่าผลลัพธ์การค้นหาข้อมูลการบันทึกสถานที่ทั้งหมดที่มีอยู่ในฐานข้อมูลของผู้ใช้โดยใช้ item เป็นรับข้อมูลในการค้นหา
\item บรรทัดที่ 13 - 16 เป็นการเช็คเงื่อนไขเมื่อเข้าเงื่อนไขก็จะเข้ามาทำงานในส่วนนี้โดยใช้ข้อมูลที่เก็บไว้ใน bookmarks มาทำการวนฟอเพื่อดูข้อมูล และก็เข้าเงื่อนไขจะเช็คดูอีกครั้งว่าค่าของ item.id มีค่าเท่ากับ place.id ไหม ถ้าตรงกันก็จะให้ลบชื่อสถานที่ตัวนั้นทิ้ง
\item บรรทัดที่ 17 - 22 เป็นการเช็คเงื่อนไขในกรณีที่ข้อมูลการบันทึกสถานที่นั้นยังไม่เคยกดบันทึกก็จะทำการเพิ่มข้อมูลสถานที่นั้นลงไปใน bookmarks
\item บรรทัดที่ 24 - 28 เป็นการอัพเดทข้อมูลในส่วนของ filed ที่ชื่อ bookmarks ในฐานข้อมูลของผู้ใช้ โดยอัพเดทผ่าน method put
\item บรรทัดที่ 29 เป็นการรีเซตค่าตรงปุ่มกดบันทึกให้เปลี่ยนเป็นบันทึก
\end{itemize}
\newpage
\section{การพัฒนาในส่วนของการแจ้งแก้ไขข้อมูล}
เมื่อผู้ใช้กดปุ่มแจ้งแก้ไข ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-edit} - \ref{Fig:4-edit1}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
editDataPlace(){
if (this.id_place !== undefined) {
this.dataPlace = {
edit_by: this.edit_by,
img_user: this.img_user,
namelocation: this.editForm.value.namelocation,
group: this.group,
address: this.editForm.value.address,
cityprovince: this.editForm.value.cityprovince,
description: this.editForm.value.description,
phonenumber: this.editForm.value.phonenumber,
price: this.editForm.value.price,
openclose: this.editForm.value.openclose,
website: this.editForm.value.website,
facebook: this.editForm.value.facebook,
lat: this.editForm.value.lat,
lng: this.editForm.value.lng,
image: this.imgs,
typeplace: this.typeplace
}
this.http.put(`${KUZZLE}:${KPORT}/${KINDEX}/dataedit/${this._id}/_update`,
this.dataPlace)
.subscribe((response) => {
console.log(response);
})}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มแจ้งแก้ไขสถานที่}
\label{Fig:4-edit}
\end{figure}
จากภาพที่ \ref{Fig:4-edit} โครงสร้างของไฟล์ detail-place.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 ฟังก์ชัน editDataPlace() ฟังก์ชันนี้จะทำงานก็ต่อเมื่อผู้ใช้กดปุ่มแจ้งแก้ไข
\item บรรทัดที่ 2 - 20 เป็นการเช็คเงือนไขดูว่าสถานที่จะแจ้งแก้ไขเคยมีประวัติการแจ้งแก้ไขไหม ถ้า this.idplace != undefined ก็จะเข้ามาทำงานในส่วนนี้โดยจะทำการเก็บข้อมูลรายละเอียดการแก้ไขไว้ในตัวแปรชื่อ dataplace
\newpage
\item บรรทัดที่ 21 - 24 เป็นการส่งข้อมูลที่ได้จาก dataplace ไปทำการอัพเดทข้อมูลการแจ้งแก้ไขเพื่อที่จะส่งไปให้แอดมินเป็นผู้อนุมัติแก้ไขข้อมูล
\end{itemize}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
else{
this.dataPlace = {
edit_by:this.edit_by,
img_user: this.img_user,
namelocation: this.editForm.value.namelocation,
group: this.group,
address: this.editForm.value.address,
cityprovince: this.editForm.value.cityprovince,
description: this.editForm.value.description,
phonenumber: this.editForm.value.phonenumber,
price: this.editForm.value.price,
openclose: this.editForm.value.openclose,
website: this.editForm.value.website,
facebook: this.editForm.value.facebook,
lat: this.editForm.value.lat,
lng: this.editForm.value.lng,
image: this.imgs,
typeplace: this.typeplace
}
this.http.post(`${KUZZLE}:${KPORT}/${KINDEX}/dataedit/${this._id}/_create`,
this.dataPlace)
.subscribe((response) => {
console.log(|\thcodedouble{เก็บข้อมูลการแจ้งแก้ไขเรียบร้อย}|,response);})
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มแจ้งแก้ไขสถานที่}
\label{Fig:4-edit1}
\end{figure}
จากภาพที่ \ref{Fig:4-edit1} โครงสร้างของไฟล์ detail-place.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 - 19 เป็นเช็คต่อจากภาพที่ \ref{Fig:4-edit} โดยในเงื่อนไขนี้คือถ้าของ idสถานที่ที่แจ้งแก้ไขมาเป็น undefined หมายความว่าสถานที่นี้ยังไม่เคยมีประวัติการแจ้งแก้ไขโดยจะทำการเก็บข้อมูลการแจ้งแก้ไขที่ได้จากผู้ใช้มาเก็บไว้ใน dataplace
\item บรรทัดที่ 20 - 24 เป็นการเรียกใช้ dataplace ที่เก็บข้อมูลการแจ้งแก้ไขไว้มาทำการเพิ่มลงไปในฐานข้อมูลของการแจ้งแก้ไข
\end{itemize}
\newpage
\section{การพัฒนาในส่วนของการค้นหาสถานที่}
เมื่อผู้ใช้กดปุ่มค้นหา ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-search} - \ref{Fig:4-search1}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
dataPlace(){
if (this.typeplace == "all") {
this._resultsPlace = []
const list = ["eatgrill","eatdrinks","eatnoodle",
"eatesan","eatjapan","eatsteak"]
list.map(item => {
const query = {
query: {
},size: 100
}
this.http.post(`${KUZZLE}:${KPORT}/${KINDEX}/${item}/_search`,
query)
.subscribe((res: any) =>{
this._resultsPlace = [ ...this._resultsPlace,...res.result.hits]
})
})
} else {
const query = {
query: {
}, size: 100
}
this.http.post(`${KUZZLE}:${KPORT}/${KINDEX}/${this.typeplace}/_search`,query)
.subscribe((res: any) => {
this._resultsPlace = res.result.hits
})
}
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มเลือกประเภท}
\label{Fig:4-search}
\end{figure}
จากภาพที่ \ref{Fig:4-search} สามารถอธิบายการทำงานของฟังก์ชันการค้นหาสถานที่ได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 ฟังก์ชัน dataPlace() จะทำงานก็ต่อเมื่อผู้ใช้กดปุ่มเลือกประเภทสถานที่
\item บรรทัดที่ 2 เป็นการเช็คเงื่อนไขดูว่า ประเภทที่ผู้ใช้เลือกนั้นเป็นประเภทไหน ถ้าผู้ใช้เลือกทั้งหมด ก็จะมาเข้าเงื่อนไขนี้
\item บรรทัดที่ 4 - 5 เป็นการประกาศ list ขึ้นมาเก็บค่าของประเภท โดยมี type เป็น array
\item บรรทัดที่ 6 - 10 เป็นกานำข้อมูลที่อยู่ใน list มาทำการวนฟอเพื่อนำไปค้นหาข้อมูลในฐานข้อมูลตามประเภททั้งหมดที่มีอยู่ใน list
\item บรรทัดที่ 13 เป็นการทำงานหลังจากที่ค้นหาข้อมูลเสร็จจะนำข้อมูลที่ได้มาเก็บไว้ใน resultsPlace
\item บรรทัดที่ 17 - 24 เป็นการเช็คเงื่อนไขในกรณีที่ประเภทที่ผู้ใช้เลือกมาไม่ใช่ทั้งหมด ก็จะทำการค้นหาข้อมูลในฐานข้อมูลตามประเภทที่ผู้ใช้เลือก เมื่อค้นหาเสร็จก็จะนำข้อมูลที่ได้มาเก็บไว้ใน resultsPlace
\end{itemize}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
onClickplace(){
if(this.name == null){
alert(|\thcodedouble{กรุณากรอกข้อมูลก่อนกดปุ่มค้นหา}|);
}else{
let data = {
id_place : this.dataplace.id ,
type_place : this.dataplace.type,
type_review : this.dataplace.type_review,
name: this.name,
}
localStorage.setItem("dataPlace",JSON.stringify(data))
this.router.navigate(['/list-place'])
}
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กดปุ่มค้นหา(ต่อ)}
\label{Fig:4-search1}
\end{figure}
จากภาพที่ \ref{Fig:4-search1} สามารถอธิบายการทำงานของฟังก์ชันการค้นหาสถานที่ได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 ฟังก์ชัน onClickplace() จะทำงานก็ต่อเมื่อผู้ใช้กดปุ่มค้นหา
\item บรรทัดที่ 2 - 3 เป็นการเช็คเงื่อนไขว่าช่องให้กรอกชื่อสถานทีจะต้องไม่เป็น !null ถ้าเป็น null จะเข้าเงื่อนไขนี้ โดยจะมีความแจ้งผู้ใช้ว่ากรุณากรอกข้อมูลก่อนกดปุ่มค้นหา
\item บรรทัดที่ 4 - 12 เป็นการเช็คเงื่อนไขในกรณีที่ผู้ใช้กรอกชื่อสถานที่เรียบร้อยก่อนกดปุ่มค้นหา ก็จะเข้ามาทำงานในส่วนนี้โดยจะประกาศต้วแปรชื่อ data ขึ้นมาเพื่อเก็บค่าของ id สถานที่ type สถานที่ type ของรีวิว โดยนำข้อมูลที่ได้ไปเขียนลง localStorage ที่มีชื่อว่า dataPlace หลังจากนั้นก็ router ไปหน้า list-place
\end{itemize}
\newpage
\section{การพัฒนาในส่วนของการทำแบบประเมิน}
เมื่อผู้ใช้ทำการกรอกแบบประเมิน ระบบจะมีการทำงาน แสดงดังรูปที่ \ref{Fig:4-question} - \ref{Fig:4-question2}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
userForm = new FormGroup({
gender: new FormControl('', Validators.required),
age: new FormControl('', Validators.required),
job: new FormControl('', Validators.required),
income: new FormControl('', Validators.required),
live: new FormControl('', Validators.required),
travel_purpose: new FormControl('', Validators.required),
travel_style: new FormControl('', Validators.required),
vehicle: new FormControl('', Validators.required),
info: new FormControl('', Validators.required),
frequency: new FormControl('', Validators.required),
travelUbon: new FormControl('', Validators.required),
goal: new FormControl('', Validators.required),
environment: new FormControl('', Validators.required),
decision: new FormControl('', Validators.required),
convenience: new FormControl('', Validators.required),
want: new FormControl('', Validators.required),
});
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กรอกแบบประเมิน}
\label{Fig:4-question}
\end{figure}
จากภาพที่ \ref{Fig:4-question} โครงสร้างไฟล์ questionnaire.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 - 18 เป็นฟอมที่ใช้เก็บค่าจากผู้ใช้เมื่อผู้ใช้เลือกช้อยส์ของแต่ละคำถามซึ่งจะมีด้วยกันทั้งหมด 16 ข้อ
\end{itemize}
\newpage
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}predict() {
this.isValidFormSubmitted = false;
if (this.userForm.invalid) {
return;
}
this.isValidFormSubmitted = true;
this.user.q1 = this.userForm.get('gender').value;
this.user.q2 = this.userForm.get('age').value;
this.user.q3 = this.userForm.get('job').value;
this.user.q4 = this.userForm.get('income').value;
this.user.q5 = this.userForm.get('live').value;
this.user.q6 = this.userForm.get('travel_purpose').value;
this.user.q7 = this.userForm.get('travel_style').value;
this.user.q8 = this.userForm.get('vehicle').value;
this.user.q9 = this.userForm.get('info').value;
this.user.q10 = this.userForm.get('frequency').value;
this.user.q11 = this.userForm.get('travelUbon').value;
this.user.q12 = this.userForm.get('goal').value;
this.user.q13 = this.userForm.get('decision').value;
this.user.q14 = this.userForm.get('environment').value;
this.user.q15 = this.userForm.get('convenience').value;
this.user.q16 = this.userForm.get('want').value;
this.resultModel = [
this.user.q1,this.user.q2,this.user.q3,
this.user.q4,this.user.q5,this.user.q6,
this.user.q7,this.user.q8,this.user.q9,
this.user.q10,this.user.q11,this.user.q12,
this.user.q13,this.user.q14,this.user.q15,this.user.q16
]
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กรอกแบบประเมินเสร็จกดปุ่มส่งข้อมูล(ต่อ)}
\label{Fig:4-question1}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-question1} โครงสร้างไฟล์ questionnaire.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 ฟังก์ชัน predict() ฟังก์ชันนี้จะทำงานก็ต่อเมื่อผู้ใช้กดปุ่มส่งข้อมูลโดยจะส่งข้อมูลที่ได้จากการกรอกแบบประเมินไปให้ฝั่ง server
\item บรรทัดที่ 2 - 5 เป็นการเช็คดูว่าฟอมที่ผู้ใช้กรอกมาตอบครบทุกข้อหรือไม ถ้ายังไม่ไม่ครบทุกข้อก็จะไม่สามารถกดส่งข้อมูลได้โดยระบบจะแจ้งข้อที่ผู้ใช้ข้าม
\item บรรทัดที่ 6 ในกรณีที่ผู้ใช้ตอบครบทุกข้อก็จะสามารถกดปุ่มส่งข้อมูลได้
\item บรรทัดที่ 7 - 22 เป็นการเก็บค่าข้อมูลคำตอบที่ผู้ใช้เลือกจาก userForm ไว้ใน user.q1 - user.q16 โดยเรียงตามข้อเริ่มจากข้อ 1 ถึง 16
\item บรรทัดที่ 24 - 30 เป็นการเรียกใช้ resultModel เพื่อเก็บค่าผลลัพธ์ของคำตอบที่ผู้ใช้เลือกซึ่งมีการเก็บเป็นแบบ array
\end{itemize}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
this.http.post('http://10.80.29.22:8000/api/predict',{
questionnaire : this.resultModel
}).subscribe((res:any) =>{
let resultmodels = {
result: this.resultModel1,
name : res.results,
number : res._predictOut
}
this.resultpredict.sendpredict(resultmodels)
this.dialog.open(DialogResultComponent,{
});
})
}
\end{minted}
\caption{การทำงานของระบบเมื่อผู้ใช้กรอกแบบประเมินเสร็จกดปุ่มส่งข้อมูล(ต่อ)}
\label{Fig:4-question2}
\end{figure}
จากภาพที่ \ref{Fig:4-question2} โครงสร้างไฟล์ questionnaire.component.ts อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 - 2 เป็นการนำข้อมูลที่ได้จาก resultModel ส่งค่าไปให้ server ที่ port 8000 ทำงานต่อในส่วนของการ predict ข้อมูล
\newpage
\item บรรทัดที่ 4 - 8 เป็นการนำเรียกข้อมูลได้จากจากฝั่ง server มาเก็บไว้ใน resultmodels โดยจะเก็บผลลัพธ์ของการกรอกแบบสอบถามไว้ใน result เก็บ ข้อมูลผลลัพธ์การ predict ไว้ที่ number
\item บรรทัดที่ 9 เป็นการเรียกใช้ service ของ sendpredict เพื่อที่จะเอาค่าข้อมูลที่เก็บไว้ที่ resultmodels ไปบันทึกไว้ที่ sendpredict
\item บรรทัดที่ 10 เป็นการเรียกใช้ dialog.open เพื่อเปิดหน้าของ DialogResultComponent มาแสดงผลลัพธ์การแนะนำ
\end{itemize}
\section{การพัฒนาในส่วนของ Server ที่ใช้ในการ train model}
เมื่อผู้ดูแลระบบกดปุ่ม train จะมีกระบวนการทำงาน แสดงดังรูปที่ \ref{Fig:4-train} - \ref{Fig:4-train2}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
app.get("/api/trian", (req, res) => {
const startTime = new Date()
axios.post('http://10.80.29.22:7512/dbpom/model/_search?size=1500',{})
.then((response) => {
const datamodel = response.data.result.hits.map((item, index) => {
var xTrains = [];
xTrains.push(item._source.q1)
xTrains.push(item._source.q2)
xTrains.push(item._source.q3)
xTrains.push(item._source.q4)
xTrains.push(item._source.q5)
xTrains.push(item._source.q6)
xTrains.push(item._source.q7)
xTrains.push(item._source.q8)
xTrains.push(item._source.q9)
xTrains.push(item._source.q10)
xTrains.push(item._source.q11)
xTrains.push(item._source.q12)
xTrains.push(item._source.q13)
xTrains.push(item._source.q14)
xTrains.push(item._source.q15)
xTrains.push(item._source.q16)
xTrain.push(xTrains);
yTrain.push(item._source.q17);
})
\end{minted}
\caption{ขั้นตอนกระบวนการ train model}
\label{Fig:4-train}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-train} โครงสร้างของไฟล์ app.js อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 เมื่อผู้ดูแลระบบกดปุ่ม train ก็จะมาเรียกใช้งานที่ path:"api/train"
\item บรรทัดที่ 2 เป็นการกำหนดเวลาเริ่มต้นการเทรน
\item บรรทัดที่ 3 เป็นการค้นหาข้อมูลจากฐานข้อมูลของ model เพื่อนำข้อมูลที่ผู้ใช้กรอกแบบประเมินมาทำการ train
\item บรรทัดที่ 4 - 25 เป็นการใช้ .map ในการวนฟอค้นหาข้อมูลทั้งหมดที่มีอยู่ในฐานข้อมูลและทำการแยกค่าของ xTrain yTrain โดย xTrain จะเก็บค่าของ q1 - q16 ส่วน yTrain จะเก็บค่าของ q17
\end{itemize}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
const XTrain = tf.tensor2d(xTrain)
const YTrain = tf.tensor2d(yTrain.map(item => [
item === 0 ? 1 : 0,
item === 1 ? 1 : 0,
item === 2 ? 1 : 0,
item === 3 ? 1 : 0,
item === 4 ? 1 : 0,
item === 5 ? 1 : 0
]));
model = tf.sequential();
model.add(tf.layers.dense({
inputShape: [16],
activation: 'sigmoid',
units: 6,
}));
model.add(tf.layers.dense({
inputShape: [16],
activation: 'sigmoid',
units: 6,
}));
model.add(tf.layers.dense({
activation: 'softmax',
units: 6,
}));
model.summary();
\end{minted}
\caption{ขั้นตอนกระบวนการ train model(ต่อ)}
\label{Fig:4-train1}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-train1} โครงสร้างของไฟล์ app.js อธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 - 9 เริ่ม load ข้อมูล xTrain , yTrain โดยค่าของ xTrain จะถูกเก็บไว้ที่ XTrain และค่าของ yTrain จะถูกเก็บไว้ที่ YTrain ซึ่งค่าของ yTrain จะมี 0,1,2,3,4,5
\item บรรทัดที่ 10 เป็นการสร้าง model จาก tf.sequential()
\item บรรทัดที่ 11 - 24 เป็นการเพิ่ม layer sigmoid และ layer softmax
\item บรรทัดที่ 25 เป็นการแสดง summary ของ model
\end{itemize}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
const learningRate = 0.01
const optimizer = tf.train.adam(learningRate);
model.compile({
optimizer: optimizer,
loss: 'meanSquaredError',
metrics: ['accuracy'],
});
let dataAcc = []
for(let i =0; i <= 1000; i++){
model.fit(XTrains, YTrains,{epochs:1000}).then((info) => {
const endTime = new Date()
dataAcc = [...dataAcc,...info.history.acc]
const maxAcc = Math.max(...dataAcc)
const modeljsonfile = `file://${__dirname}/models/model`
model.save(modeljsonfile).then(() => {
})
.catch((error) => {
})
});
}
\end{minted}
\caption{ขั้นตอนกระบวนการ train model(ต่อ)}
\label{Fig:4-train2}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-train2} โครงสร้างของไฟล์ app.js สามารถอธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 - 7 เป็นการเริ่ม train model โดยใช้ adam ซึ่งค่า learningRate ที่ใช้ 0.01
\item บรรทัดที่ 8 เป็นการประกาศ dataAcc มาเก็บค่าของ accuracy
\item บรรทัดที่ 9 เป็นการเพิ่มรอบในการเทรน โดยจะเทรนทั้งหมด 1000 รอบ
\item บรรทัดที่ 10 เป็นการสิ้นสุดการเทรนได้โมเดล
\item บรรทัดที่ 11 เป็นการเก็บเวลาสิ้นสุดในการเทรน
\item บรรทัดที่ 12 เป็นการเก็บค่า accuracy มาไว้ใน array dataAcc
\item บรรทัดที่ 13 เป็นการหาค่าสูงสุดของ accuracy
\item บรรทัดที่ 14 - 16 เป็นการนำโมเดลที่ได้มาบันทึกเป็นไฟล์ json ไว้ที่โฟล์เดอร์ models
\item บรรทัดที่ 17 - 18 เป็นการแสดงเออเร่อในกรณีที่บันทึกไฟล์โมเดลไม่สำเร็จ
\end{itemize}
\newpage
\section{การพัฒนาในส่วนของ Server ที่ใช้ในการ predict ข้อมูล}
เมื่อผู้ใช้กดปุ่มส่งข้อมูล จะมีกระบวนการทำงาน แสดงดังรูปที่ \ref{Fig:4-predict}
\begin{figure}[H]
\begin{minted}[escapeinside=||,frame=lines,framesep=2mm,baselinestretch=1.15,fontsize=\footnotesize,linenos]{javascript}
app.post("/api/predict",(req, res) => {
const result = req.body.questionnaire.map(item => parseInt(item))
const xTest = tf.tensor2d([result])
const modeljsonfile = `file://${__dirname}/models/model/model.json`
tf.loadLayersModel(modeljsonfile)
.then((_model) => {
const predictOut = _model.predict(xTest)
const category = predictOut.dataSync().map(_item =>
Math.round((parseInt(_item * 1000)/1000)*100));
category.map((item,index)=> {
dataShow.push({
label : nameResult[index]+' '+item+'%',
value : item,index
})
})
dataShow = dataShow.sort(
(a, b) => (a.value > b.value) ? 1 : ((b.value > a.value) ? -1 : 0)
).reverse()
return res.send({
predictOut: predictOut,
category: category,
data : req.body.questionnaire,
results : dataShow
})
})
\end{minted}
\caption{ขั้นตอนกระบวนการ predict ข้อมูล}
\label{Fig:4-predict}
\end{figure}
\newpage
จากภาพที่ \ref{Fig:4-predict} โครงสร้างของไฟล์ app.js สามารถอธิบายการทำงานได้ดังนี้
\begin{itemize}[label={--}]
\item บรรทัดที่ 1 เมื่อผู้ใช้กดปุ่มส่งข้อมูล ก็จะมาเรียกใช้งานที่ path:"api/predict" เพื่อนำข้อมูลที่ได้ส่งให้ฝั่ง server ทำการประมวลให้
\item บรรทัดที่ 2 เป็นการประการ result ขึ้นมาเพื่อมาเก็บข้อมูลแบบประเมินที่ผู้ใช้กรอกเข้ามาในฝั่งของ Client ซึ่งค่าที่ส่งมาเป็น string เราจะต้องมาแปลงให้เป็นตัวเลขก่อนโดยใช้คำสั่ง parseInt()
\item บรรทัดที่ 3 เป็นการนำเอา result มาทำให้เป็นข้อมูล 2 มิติ และเก็บไว้ที่ xTest
\item บรรทัดที่ 4 - 6 เป็นการโหลดไฟล์โมเดลมาใช้งานในการทำนายผลลัพธ์
\item บรรทัดที่ 7 เป็นการนำเอาตัวแปร xTest มาทำการ predict ข้อมูลและเก็บไว้ที่ predictOut
\item บรรทัดที่ 8 - 9 เป็นการเรียกใช้ predictOut มาทำการวิเคราะห์ผลลัพธ์การแนะนำโคยค่าทีได้จะออกมาเป็นตัวเลขทศนิยมซึ่งจะต้องนำค่ามาคิดเป็นเปอร์เซ็นต์และเก็บไว้ที่ตัวแปร category
\item บรรทัดที่ 10 - 15 เป็นการนำค่าของ category มาค้นหาและไป push เก็บไว้ในรูปแบบ label และ value ซึ่ง label จะเก็บชื่อของหมวดและค่าเปอร์เซ็นต์ ส่วน value จะเก็บตำแหน่งของชื่อหมวด และเก็บไว้ใน dataShow
\item บรรทัดที่ 16 - 18 เป็นการนำเอาค่าข้อมูลที่เก็บไว้ใน dataShow มาทำการจัดอันดับซึ่งเรียงจากค่าที่มีเปอร์เซ็นต์มากสุดไปน้อยสุด
\item บรรทัดที่ 19 - 25 เป็นการส่งค่าผลลัพธ์ที่ได้กลับไปให้ฝั่ง Client เพื่อทำการแสดงผลข้อมูลการแนะนำให้กับผู้ใช้ได้ทราบ
\end{itemize}
\newpage
\ No newline at end of file
\chapter{การทดสอบระบบ}
หลังจากที่ได้ผ่านขั้นตอนสำหรับการพัฒนาระบบทั้งหมดแล้ว ขั้นตอนต่อไปคือการทดสอบระบบเพื่อตรวจสอบการทำงานของแต่ละฟังก์ชันกันรวมถึงตรวจสอบความถูกต้องของการทำงาน
ทั้งระบบเพื่อให้การพัฒนาโปรแกรมเป็นไปอย่างมีประสิทธิภาพ ทดสอบระบบเพื่อหาข้อผิดพลาดและแก้ไขข้อบกพร่องนั้นก่อนการใช้งานจริง โดยในการทดสอบระบบจะแบ่งเป็น 2 ส่วน ได้แก่
การทดสอบในส่วนฟังก์ชันของระบบ และการทดสอบประสิทธิภาพของโมเดล
\section{การทดสอบในส่วนฟังก์ชันของระบบ}
\subsection{ผลการทดสอบการสมัครสมาชิก}
\begin{table}[H]
\caption{ผลการทดสอบการสมัครสมาชิก}
\centering
\label{tab:test1}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการสมัครสมาชิก}
& \setstretch{1.0}{ผู้ใช้งานเข้ามาในหน้าสมัครสมาชิก}
& \setstretch{1.0}{ระบบแสดงหน้าสมัครสมาชิก}\\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้งานกดปุ่มเลือกรูปภาพ}
& \setstretch{1.0}{ระบบแสดงหน้าให้เลือกรูปภาพ}\\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มสมัครสมาชิกโดยกรอกชื่อซ้ำกับชื่อที่มีอยู่ในระบบ}
& \setstretch{1.0}{มีข้อความแสดงบอกผู้ใช้ว่า "username นี้มีผู้ใช้ไปแล้ว"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มสมัครสมาชิกโดยกรอกชื่อไม่ซ้ำกับชื่อที่มีอยู่ในระบบ}
& \setstretch{1.0}{มีข้อความแสดงบอกผู้ใช้ว่า "สมัครสมาชิกสำเร็จ"} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบการเข้าสู่ระบบ}
\begin{table}[H]
\caption{ผลการทดสอบการเข้าสู่ระบบ}
\centering
\label{tab:test2}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการเข้าสู่ระบบ}
& \setstretch{1.0}{ผู้ใช้เข้ามาในหน้าเข้าสู่ระบบ}
& \setstretch{1.0}{ระบบแสดงหน้าเข้าสู่ระบบ} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเข้าสู่ระบบ โดยไม่กรอก username และ password }
& \setstretch{1.0}{ระบบแสดงข้อความบอกผู้ใช้ว่า "กรุณากรอก username และ password ก่อนกดปุ่มเข้าสู่ระบบ"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเข้าสู่ระบบโดยกรอก username ไม่ถูกต้อง}
& \setstretch{1.0}{ระบบแสดงข้อความบอกผู้ใช้ว่า "เข้าสู่ระบบไม่สำเร็จ ชื่อผู้ใช้หรือรหัสผ่านไม่ตรงกัน กรุณาเข้าสู่ระบบใหม่อีกครั้ง"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเข้าสู่ระบบโดยกรอก username ถูกต้องแต่กรอก password ไม่ถูกต้อง}
& \setstretch{1.0}{ระบบแสดงข้อความบอกผู้ใช้ว่า "เข้าสู่ระบบไม่สำเร็จ ชื่อผู้ใช้หรือรหัสผ่านไม่ตรงกัน กรุณาเข้าสู่ระบบใหม่อีกครั้ง"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเข้าสู่ระบบโดยกรอก username และ password ถูกต้อง}
& \setstretch{1.0}{ระบบแสดงข้อความบอกผู้ใช้ว่า "เข้าสู่ระบบสำเร็จ" และจะแสดงชื่อผู้ใช้รูปโปรไฟล์ที่แถบบนด้านขวา} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้าจัดอับดับสถานที่ยอดนิยม}
\begin{table}[H]
\caption{ผลการทดสอบหน้าจัดอับดับสถานที่ยอดนิยม}
\centering
\label{tab:test3}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบหน้าจัดอันดับสถานที่ยอดนิยม}
& \setstretch{1.0}{ผู้ใช้กดเลือกเมนูสถานที่ยอดนิยม}
& \setstretch{1.0}{ระบบแสดงหน้าสถานที่ยอดนิยม โดยจะมีหมวดให้ผู้ใช้เลือกอยุ่ 3 หมวด ได้แก่ สถานที่ท่องเที่ยว ร้านอาหารและที่พัก}\\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกหมวด}
& \setstretch{1.0}{ระบบแสดงผลการจัดอันดับของหมวดทีผู้ใช้เลือก} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้คลิกที่ชื่อสถานที่}
& \setstretch{1.0}{ระบบเปลี่ยนหน้าเว็บไปหน้าแสดงรายละเอียดข้อมูลสถานที่} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้าหมวดที่เที่ยว}
\begin{table}[H]
\caption{ผลการทดสอบหน้าหมวดที่เที่ยว}
\centering
\label{tab:test4}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบหน้าหมวดที่เที่ยว}
& \setstretch{1.0}{ผู้ใช้กดเลือกเมนูที่เที่ยว}
& \setstretch{1.0}{ระบบแสดงหน้าหมวดที่เที่ยวซึ่งประกอบด้วย 6 หมวดได้แก่ หมวดน้ำตก หมวดสวนน้ำและสวนสัตว์ หมวดสถานที่บันเทิง หมวดอุทยานและธรรมชาติ หมวดถนนคนเดิน และสุดท้ายหมวดสถานบันเทิง} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกประเภทแต่ไม่กรอกชื่อสถานที่แล้วกดปุ่มค้นหา}
& \setstretch{1.0}{ระบบแสดงข้อความแจ้งผู้ใช้ว่า "กรุณากรอกชื่อสถานที่ก่อนกดปุ่มค้นหา"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกประเภทและกรอกชื่อสถานที่แล้วกดปุ่มค้นหา}
& \setstretch{1.0}{ระบบแสดงหน้าข้อมูลสถานที่ที่ผู้ใช้ทำการค้นหา} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกหมวด}
& \setstretch{1.0}{ระบบเปลี่ยนหน้าเว็บไปหน้าแสดงรายชื่อสถานที่ตามหมวดที่ผู้ใช้เลือก} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้าหมวดที่กิน}
\begin{table}[H]
\caption{ผลการทดสอบหน้าหมวดที่กิน}
\centering
\label{tab:test5}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบหน้าหมวดที่กิน}
& \setstretch{1.0}{ผู้ใช้กดเลือกเมนูที่กิน}
& \setstretch{1.0}{ระบบแสดงหน้าหมวดที่กินซึ่งประกอบด้วย 6 หมวดได้แก่ หมวดบุฟเฟ่ต์ปิ้งย่าง หมวดคาเฟ่ หมวดเส้น หมวดอาหารท้องถิ่น หมวดอาหารญี่ปุ่น และสุดท้ายหมวดสเต็ก} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกประเภทแต่ไม่กรอกชื่อสถานที่แล้วกดปุ่มค้นหา}
& \setstretch{1.0}{ระบบแสดงข้อความแจ้งผู้ใช้ว่า "กรุณากรอกชื่อสถานที่ก่อนกดปุ่มค้นหา"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกประเภทและกรอกชื่อสถานที่แล้วกดปุ่มค้นหา}
& \setstretch{1.0}{ระบบแสดงหน้าข้อมูลสถานที่ที่ผู้ใช้ทำการค้นหา} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกหมวด}
& \setstretch{1.0}{ระบบเปลี่ยนหน้าเว็บไปหน้าแสดงรายชื่อสถานที่ตามหมวดที่ผู้ใช้เลือก}\\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้าหมวดที่พัก}
\begin{table}[H]
\caption{ผลการทดสอบหน้าหมวดที่พัก}
\centering
\label{tab:test6}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบหน้าหมวดที่พัก}
& \setstretch{1.0}{ผู้ใช้กดเลือกเมนูที่พัก}
& \setstretch{1.0}{ระบบแสดงหน้าหมวดที่พักซึ่งประกอบด้วย 6 หมวดได้แก่ หมวดที่พักวิลล่า หมวดที่พักโรงแรม หมวดที่พักรีสอร์ท หมวดที่พักกางเต้นท์ หมวดที่พักบังกะโล/เกสท์เฮาส์ และสุดท้ายหมวดที่พักโฮมสเตย์} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกประเภทแต่ไม่กรอกชื่อสถานที่แล้วกดปุ่มค้นหา}
& \setstretch{1.0}{ระบบแสดงข้อความแจ้งผู้ใช้ว่า "กรุณากรอกชื่อสถานที่ก่อนกดปุ่มค้นหา"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกประเภทและกรอกชื่อสถานที่แล้วกดปุ่มค้นหา}
& \setstretch{1.0}{ระบบแสดงหน้าข้อมูลสถานที่ที่ผู้ใช้ทำการค้นหา} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกหมวด}
& \setstretch{1.0}{ระบบเปลี่ยนหน้าเว็บไปหน้าแสดงรายชื่อสถานที่ตามหมวดที่ผู้ใช้เลือก} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้าแสดงสถานที่}
\begin{table}[H]
\caption{ผลการทดสอบหน้าแสดงสถานที่}
\centering
\label{tab:test7}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบหน้าแสดงสถานที่}
& \setstretch{1.0}{รับข้อมูลที่ส่งจากหน้าหมวดที่เที่ยว ที่กิน ที่พัก}
& \setstretch{1.0}{ระบบแสดงหน้าสถานที่} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเพิ่มสถานที่ ในกรณีที่ไม่ได้เข้าสู่ระบบ}
& \setstretch{1.0}{ระบบแสดงข้อความแจ้งผู้ใช่ว่า "กรุณาล็อคอินเข้าสู่ระบบก่อนกดปุ่มเพิ่มสถานที่"}\\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเพิ่มสถานที่ ในกรณีเข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ระบบแสดงกล่องข้อความหน้าเพิ่มสถานที่} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกสถานที่ที่สนใจ}
& \setstretch{1.0}{ระบบเปลี่ยนหน้าเว็บไปแสดงหน้ารายละเอียดข้อมูลสถานที่} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้าแสดงรายละเอียดสถานที่}
\begin{table}[H]
\caption{ผลการทดสอบหน้าแสดงรายละเอียดสถานที่}
\centering
\label{tab:test8}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบหน้าแสดงรายละเอียดสถานที่}
& \setstretch{1.0}{รับข้อมูลที่ส่งมาจากหน้าแสดงสถานที่}
& \setstretch{1.0}{ระบบแสดงหน้ารายละเอียดสถานที่} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเขียนรีวิว ในกรณีที่ไม่ได้เข้าสู่ระบบ}
& \setstretch{1.0}{ระบบจะแสดงข้อความแจ้งผู้ใช้ว่า "กรุณาล็อคอินเข้าสู่ระบบก่อนกดปุ่มรีวิว"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเขียนรีวิว ในกรณีที่เข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ระบบจะแสดงป็อปอัพหน้าเขียนรีวิวขึ้นมาให้ผู้ใช้เขียนรีวิว}\\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มบันทึก ในกรณีที่ไม่ได้เข้าสู่ระบบ}
& \setstretch{1.0}{ระบบจะแสดงข้อความแจ้งผู้ใช้ว่า "กรุณาล็อคอินเข้าสู่ระบบก่อนกดปุ่มบันทึกสถานที่"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มบันทึก ในกรณีที่เข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ผู้ใช้สามารถกดบันทึกสถานที่ได้และระบบจะนำข้อมูลการบันทึกไปแสดงที่หน้าโปรไฟล์ของผู้ใช้} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มแจ้งแก้ไข ในกรณีไม่ได้เข้าสู่ระบบ}
& \setstretch{1.0}{ระบบจะแสดงข้อความแจ้งผู้ใช้ว่า "กรุณาล็อคอินเข้าสู่ระบบก่อนกดปุ่มแจ้งแก้ไข"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มแจ้งแก้ไข ในกรณีเข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ระบบจะแสดงป็อปอัพหน้าแจ้งแก้ไขข้อมูลสถานที่ขึ้นมาให้ผู้ใช้กรอกข้อมูลเพื่อแจ้งแก้ไข} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้าเขียนรีวิว}
\begin{table}[H]
\caption{ผลการทดสอบหน้าเขียนรีวิว}
\centering
\label{tab:test9}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการเขียนรีวิวและให้คะแนนสถานที่}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเขียนรีวิว ที่หน้าแสดงรายละเอียดสถานที่ ในกรณีที่ไม่ได้เข้าสู่ระบบ}
& \setstretch{1.0}{ระบบจะแสดงข้อความแจ้งผู้ใช้ว่า "กรุณาล็อคอินเข้าสู่ระบบก่อนกดปุ่มรีวิว"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเขียนรีวิว ที่หน้าแสดงรายละเอียดสถานที่ ในกรณีที่เข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ระบบจะแสดงกล่องข้อความเขียนรีวิวขึ้นมาให้ผู้ใช้เขียนรีวิว} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้ให้คะแนน กรอกหัวข้อรีวิว กรอกรายละเอียดแต่ไม่ได้เลือกรูปภาพและกดปุ่มบันทึกรีวิว}
& \setstretch{1.0}{ระบบบันทึกข้อมูลที่รีวิวลงในฐานข้อมูลของสถานที่} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้ให้คะแนน กรอกหัวข้อรีวิว กรอกรายละเอียด เลือกรูปภาพและกดปุ่มบันทึกรีวิว}
& \setstretch{1.0}{ระบบบันทึกข้อมูลที่รีวิวลงในฐานข้อมูลของสถานที่} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มยกเลิก}
& \setstretch{1.0}{ระบบจะปิดกล่องข้อความ}\\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้ากรอกแบบประเมิน}
\begin{table}[H]
\caption{ผลการทดสอบหน้ากรอกแบบประเมิน}
\centering
\label{tab:test10}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบหน้ากรอกแบบประเมิน}
& \setstretch{1.0}{ผู้ใช้เลือกเมนูแนะนำสถานที่}
& \setstretch{1.0}{ระบบแสดงหน้ากรอกแบบประเมิน} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้ทำแบบประเมินไม่ครบทุกข้อ และกดปุ่มส่งข้อมูล}
& \setstretch{1.0}{ระบบจะแสดงข้อความตัวหนังสือสีแดงตรงข้อที่ผู้ใช้ข้ามและบังคับให้กรอกข้อมูลให้ครบ} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้ทำแบบประเมินครบทุกข้อ และกดปุ่มส่งข้อมูล}
& \setstretch{1.0}{ผู้ใช้สามารถกดปุ่มส่งข้อมูลได้} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้าแสดงผลลัพธ์การแนะนำหมวดท่องเที่ยว}
\begin{table}[H]
\caption{ผลการทดสอบหน้าแสดงผลลัพธ์การแนะนำหมวดท่องเที่ยว}
\centering
\label{tab:test11}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการทำแบบประเมินเพื่อดูผลลัพธ์การแนะนำหมวดท่องเที่ยว}
& \setstretch{1.0}{ผู้ใช้ทำแบบประเมินไม่ครบทุกข้อ และกดปุ่มส่งข้อมูล}
& \setstretch{1.0}{ระบบจะแสดงข้อความตัวหนังสือสีแดงตรงข้อที่ผู้ใช้ข้ามและบังคับให้กรอกข้อมูลให้ครบ} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้ทำแบบประเมินครบทุกข้อ และกดปุ่มส่งข้อมูล}
& \setstretch{1.0}{ผู้ใช้สามารถกดปุ่มส่งข้อมูลได้และระบบจะแสดงกล่อองข้อความผลลัพธ์การแนะนำหมวดท่องเที่ยว} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกหมวดท่องเที่ยวที่ชื่นชอบและกดปุ่มใช่}
& \setstretch{1.0}{ระบบจะบันทึกข้อมูลการทำแบบประเมินของผู้ใช้ลงในฐานข้อมูลและทำการปิดกล่องแสดงผลลัพธ์การแนะนำลง} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มยกเลิก}
& \setstretch{1.0}{ระบบจะปิดกล่องแสดงผลลัพธ์การแนะนำลง} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบหน้าเพิ่มสถานที่}
\begin{table}[H]
\caption{ผลการทดสอบหน้าเพิ่มสถานที่}
\centering
\label{tab:test12}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการเพิ่มข้อมูลสถานที่ในส่วนของผู้ใช้}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเพิ่มสถานที่ ในกรณีที่ไม่ได้เข้าสู่ระบบ}
& \setstretch{1.0}{ระบบแสดงข้อความแจ้งผู้ใช่ว่า "กรุณาล็อคอินเข้าสู่ระบบก่อนกดปุ่มเพิ่มสถานที่"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเพิ่มสถานที่ ในกรณีที่เข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ระบบแสดงกล่องข้อความหน้าเพิ่มสถานที่} \\ \cline{2-3}
& \setstretch{1.0}{ในกรณีที่ผู้ใช้ไม่ได้เลือกประเภทของสถานที่ และผู้ใช้กรอกข้อมูลชื่อสถานที่ ที่อยู่ รายละเอียดสถานที่ เบอร์โทรศัพท์ เว็บไซต์ (website) เฟสบุ๊ค(facebook) ละติจูด(latitude) ลองจิจูด (longitude) และเลือกรูปภาพ แล้วกดปุ่มบันทึก }
& \setstretch{1.0}{การเพิ่มข้อมูลสถานทีล้มเหลว เนื่องจากระบบไม่สามารถทราบได้ว่าข้อมูลที่ผู้ใช้เพิ่มเข้ามาเป็นข้อมูลประเภทไหน} \\ \cline{2-3}
& \setstretch{1.0}{ในกรณีที่ผู้ใช้เลือกประเภทของสถานที่ และผู้ใช้กรอกข้อมูลชื่อสถานที่ ที่อยู่ รายละเอียดสถานที่ เบอร์โทรศัพท์ เว็บไซต์ (website) เฟสบุ๊ค(facebook) ละติจูด(latitude) ลองจิจูด (longitude) และเลือกรูปภาพ แล้วกดปุ่มบันทึก}
& \setstretch{1.0}{ระบบจะบันทึกข้อมูลการเพิ่มสถานที่ลงในฐานข้อมูลตามประเภทที่ผู้ใช้เลือก} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มยกเลิก}
& \setstretch{1.0}{ระบบจะปิดกล่องข้อความหน้าเพิ่มสถานที่ลง} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบการแสดงหน้าโปรไฟล์}
\begin{table}[H]
\caption{ผลการทดสอบการแสดงหน้าโปรไฟล์}
\centering
\label{tab:test13}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการแสดงข้อมูลโปรไฟล์}
& \setstretch{1.0}{ผู้ใช้กดเลือกเมนูโปรไฟล์}
& \setstretch{1.0}{ระบบแสดงหน้าข้อมูลโปรไฟล์ของผู้ใช้} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มแก้ไขโปรไฟล์}
& \setstretch{1.0}{ระบบจะแสดงกล่องข้อความหน้าแก้ไขโปรไฟล์ซึ่งข้อมูลที่แสดงในหน้านั้นจะมี รูปประจำตัวของผู้ใช้ ชื่อผู้ใช้มีปุ่มเปลี่ยนรหัสผ่าน ปุ่มเปลี่ยนรูปประจำตัว และปุ่มบันทึก} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกที่บันทึกไว้ ในกรณีที่ผู้ใช้ยังไม่มีข้อมูลสถานที่ที่บันทึกไว้}
& \setstretch{1.0}{ระบบจะแสดงข้อความบนหน้าเว็บว่า "ไม่มีกิจกรรมการบันทึก"}\\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกที่บันทึกไว้ ในกรณีที่ผู้ใช้มีข้อมูลสถานที่ที่บันทึกไว้}
& \setstretch{1.0}{ระบบจะแสดงข้อมูลสถานที่ที่ผู้ใช้ได้บันทึกไว้} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกรีวิว ในกรณีที่ผู้ใช้ยังไม่มีข้อมูลการรีวิวสถานที่}
& \setstretch{1.0}{ระบบจะแสดงงข้อความบนหน้าเว็บว่า "ไม่มีกิจกรรมรีวิว"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกรีวิว ในกรณีที่ผู้ใช้ยังมีข้อมูลการรีวิวสถานที่}
& \setstretch{1.0}{ระบบจะแสดงข้อมูลสถานที่ที่ผู้ใช้เคยเขียนรีวิว} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกสถานที่เพิ่ม ในกรณีที่ผู้ใช้ยังไม่เคยเพิ่มข้อมูลสถานที่}
& \setstretch{1.0}{ระบบจะแสดงงข้อความบนหน้าเว็บว่า "ไม่มีกิจกรรมเพิ่มสถานที่"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดเลือกสถานที่เพิ่ม ในกรณีที่ผู้ใช้เคยเพิ่มข้อมูลสถานที่}
& \setstretch{1.0}{ระบบจะแสดงข้อมูลสถานที่ที่ผู้ใช้ได้เพิ่ม}\\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบการแก้ไขรหัสผ่าน}
\begin{table}[H]
\caption{ผลการทดสอบการแก้ไขรหัสผ่าน}
\centering
\label{tab:test14}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการแก้ไขรหัสผ่าน}
& \setstretch{1.0}{ผู้ใช้กดปุ่มเปลี่ยนรหัสผ่าน}
& \setstretch{1.0}{ระบบแสดงหน้าเกล่องข้อความหน้าแก้ไขรหัสผ่าน} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กรอกรหัสผ่านเก่าและกรอกรหัสผ่านใหม่ แล้วกดปุ่มบันทึกการเปลี่ยนแปลงในกรณีที่ผู้ใช้กรอกรหัสผ่านเก่าไม่ถูกต้อง}
& \setstretch{1.0}{ระบบจะแสดงข้อความแจ้งผู้ใช้ว่า "เปลี่ยนรหัสผ่านไม่สำเร็จ เนื่องจากกรอกรหัสผ่านเก่าไม่ถูกต้องและรหัสยืนยันไม่ตรงกันกับรหัสผ่านใหม่"} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กรอกรหัสผ่านเก่าและกรอกรหัสผ่านใหม่ แล้วกดปุ่มบันทึกการเปลี่ยนแปลงในกรณีที่ผู้ใช้กรอกรหัสผ่านเก่าถูกต้อง}
& \setstretch{1.0}{ระบบจะแสดงข้อความแจ้งผู้ใช้ว่า "เปลี่ยนรหัสผ่านสำเร็จ รหัสผ่านใหม่คือ : "} \\ \cline{2-3}
& \setstretch{1.0}{ในกรณีที่ผู้ใช้กรอกรหัสผ่านใหม่และกรอกรหัสผ่านยืนยันไม่ตรงกัน แล้วกดปุ่มบันทึกการเปลี่ยนแปลง}
& \setstretch{1.0}{ระบบจะแสดงข้อความแจ้งผู้ใช้ว่า "เปลี่ยนรหัสผ่านไม่สำเร็จ เนื่องจากกรอกรหัสยืนยันไม่ตรงกันกับรหัสผ่านใหม่"} \\ \cline{2-3}
& \setstretch{1.0}{ในกรณีที่ผู้ใช้กรอกรหัสผ่านใหม่และกรอกรหัสผ่านยืนยันตรงกัน แล้วกดปุ่มบันทึกการเปลี่ยนแปลง}
& \setstretch{1.0}{ระบบจะแสดงข้อความแจ้งผู้ใช้ว่า "เปลี่ยนรหัสผ่านสำเร็จ รหัสผ่านใหม่คือ : "} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบลบสถานที่}
\begin{table}[H]
\caption{ผลการทดสอบลบสถานที่}
\centering
\label{tab:test15}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบลบสถานที่ในกรณีที่ผู้ใช้เป็นคนเพิ่มสถานที่เอง}
& \setstretch{1.0}{ผู้ใช้เข้าไปที่หน้าโปรไฟล์แล้วกดเลือกสถานที่เพิ่ม หลังจากนั้นผุ้ใช้คลิกสถานที่แล้วเลือกเมนูลบ}
& \setstretch{1.0}{ระบบแสดงกล่องข้อความถามผู้ใช้ว่า "คุณแน่ใจไหมว่าจะลบข้อมูลสถานที่นี้ทิ้ง" และมีปุ่มให้ผู้ใช้เลือกกดอยู่ 2 อย่างคือ แน่ใจ และ ไม่แน่ใจ} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มแน่ใจ}
& \setstretch{1.0}{ระบบทำการลบข้อมูลสถานที่ออกจากฐานข้อมูลและลบข้อมูลสถานที่ออกจากข้อมูลของผู้ใช้} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มไม่แน่ใจ}
& \setstretch{1.0}{ระบบจะปิดกล่องข้อความลง} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบลบรีวิว}
\begin{table}[H]
\caption{ผลการทดสอบลบรีวิว}
\centering
\label{tab:test16}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบลบรีวิว ในกรณีเป็นรีวิวที่ตัวเองเป็นคนเขียน}
& \setstretch{1.0}{ผู้ใช้เข้าไปที่หน้าโปรไฟล์แล้วกดเลือกรีวิว หลังจากนั้นผู้ใช้คลิกสถานที่แล้วเลือกเมนูลบรีวิว}
& \setstretch{1.0}{ระบบแสดงกล่องข้อความถามผู้ใช้ว่า "คุณแน่ใจไหมว่าจะลบความคิดเห็นของคุณทิ้ง" และจะมีปุ่มให้ผู้ใช้เลือกกดอยู่ 2 อย่างคือ แน่ใจ และ ไม่แน่ใจ} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มแน่ใจ}
& \setstretch{1.0}{ระบบทำการลบรีวิวของผู้ใช้คนนั้นออกจากข้อมูลสถานที่ในฐานข้อมูลและลบข้อมูลรีวิวนั้นออกจากข้อมูลของผู้ใช้} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ใช้กดปุ่มไม่แน่ใจ}
& \setstretch{1.0}{ระบบจะปิดกล่องข้อความลงและข้อมูลรีวิวไม่ถูกลบ} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบการจัดการแก้ไขข้อมูลสถานที่}
\begin{table}[H]
\caption{ผลการทดสอบการจัดการแก้ไขข้อมูลสถานที่}
\centering
\label{tab:test17}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการแก้ไขข้อมูลกรณีที่ผู้ดูแลระบบเข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ผู้ดูแลระบบกดปุ่มแก้ไข}
& \setstretch{1.0}{ระบบแสดงหน้าแก้ไขข้อมูล} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ดูแลระบบกรอกข้อมูลแก้ไขสถานที่แล้วกดปุ่มบันทึกการแก้ไข}
& \setstretch{1.0}{ระบบบันทึกข้อมูลการแก้ไขและอัพเดทข้อมูลลงฐานข้อมูล} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ดูแลระบบกดปุ่มยกเลิก}
& \setstretch{1.0}{ระบบปิดหน้าแก้ไขข้อมูลลงและข้อมูลไม่ได้รับการแก้ไข} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบการจัดการลบข้อมูลสถานที่}
\begin{table}[H]
\caption{ผลการทดสอบการจัดการลบข้อมูลสถานที่}
\centering
\label{tab:test18}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการลบข้อมูลสถานที่ กรณีที่ผู้ดูแลระบบเข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ผู้ดูแลระบบกดปุ่มลบ}
& \setstretch{1.0}{ระบบแสดงข้อความถามผู้ดูแลระบบว่า “คุณต้องการลบสถานที่นี้ใช่หรือไม่” และมีปุ่มตัวเลือกให้ผู้ใช้เลือกคือ ใช่ และไม่ใช่} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ดูแลระบบกดปุ่มใช่}
& \setstretch{1.0}{ระบบลบข้อมูลสถานที่ที่ผู้ดูแลระบบกดลบออกจากฐานข้อมูล} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ดูแลระบบกดปุ่มไม่ใช่}
& \setstretch{1.0}{ข้อมูลสถานที่ไม่ถูกลบออกจากฐานข้อมูล} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบการจัดการเพิ่มสถานที่}
\begin{table}[H]
\caption{ผลการทดสอบการจัดการเพิ่มสถานที่}
\centering
\label{tab:test19}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการเพิ่มสถานที่ กรณีที่ผู้ดูแลระบบเข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ผู้ดูแลระบบกดปุ่มเพิ่มสถานที่}
& \setstretch{1.0}{ระบบเปิดหน้าเพิ่มข้อมูลสถานที่} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ดูแลระบบกรอกข้อมูลรายละเอียดสถานที่ครบและกดปุ่มเพิ่มสถานที่}
& \setstretch{1.0}{ระบบจะบันทึกข้อมูลรายละเอียดสถานที่ลงในฐานข้อมูลตามประเภทของสถานที่} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ดูแลระบบกดปุ่มยกเลิก}
& \setstretch{1.0}{ระบบจะปิดหน้าเพิ่มข้อมูลสถานที่ลงและข้อมูลจะไม่ถูกเพิ่มลงในระบบ} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบจัดการข้อมูลแจ้งแก้ไข}
\begin{table}[H]
\caption{ผลการทดสอบจัดการข้อมูลแจ้งแก้ไข}
\centering
\label{tab:test20}
\begin{tabular}{ | p{4.5cm} | p{4.5cm} | p{4.5cm} | }
\hline
{\multicolumn{1}{c}{\centering การทดสอบ}} & {\multicolumn{1}{c}{\centering เงื่อนไขการทดสอบ}} & {\multicolumn{1}{c}{\centering ผลการทดสอบ}} \\ \hline
\setstretch{1.0}{ทดสอบการแก้ไขข้อมูลจากข้อมูลที่ผู้ใช้แจ้งแก้ไขมา กรณีที่ผู้ดูแลระบบเข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ผู้ดูแลระบบเลือกเมนูแจ้งแก้ไข}
& \setstretch{1.0}{ระบบแสดงหน้าข้อมูลแจ้งแก้ไข โดยจะแสดงชื่อผู้แจ้งแก้ไข ชื่อสถานที่ และปุ่มแก้ไข} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ดูแลระบบกดแก้ไข}
& \setstretch{1.0}{ระบบแสดงหน้าแก้ไขข้อมูลสถานที่โดยจะมีข้อมูลที่ผู้ใช้กรอกเข้ามาเพื่อทำการแก้ไข} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ดูแลระบบกดปุ่มบันทึกการแก้ไข}
& \setstretch{1.0}{ระบบทำการบันทึกข้อมูลการแก้ไขและอัพเดทข้อมูลการแก้ไขสถานที่ลงในฐานข้อมูล} \\ \cline{2-3}
& \setstretch{1.0}{ผู้ดูแลระบบกดปุ่มยกเลิก}
& \setstretch{1.0}{ข้อมูลจะไม่ถูกทำการแก้ไข} \\ \hline
\end{tabular}
\end{table}
\subsection{ผลการทดสอบการ Train Model}
\begin{table}[H]
\caption{ผลการทดสอบการ Train Model}
\centering
\label{tab:test21}
\begin{tabular}{ | p{4cm} | p{4.5cm} | p{4.5cm} | }
\hline
\multicolumn{1}{|c|}{การทดสอบ} & \multicolumn{1}{c|}{เงื่อนไขการทดสอบ} & \multicolumn{1}{c|}{ผลการทดสอบ} \\ \hline
\setstretch{1.0}{ทดสอบการ Train Model กรณีที่ผู้ดูแลระบบเข้าสู่ระบบสำเร็จ}
& \setstretch{1.0}{ผู้ดูแลระบบกดเลือกเมนู Train}
& \setstretch{1.0}{ระบบทำการเรียกใช้เซิร์ฟเวอร์ในการ train model เมื่อ train model สำเร็จระบบแจ้งข้อความบอกผู้ดูแลระบบว่า "train model สำเร็จ ได้โมเดลแล้ว"} \\ \hline
\end{tabular}
\end{table}
\section{การทดสอบประสิทธิภาพของโมเดล}
\subsection{ผลการทดสอบความถูกต้องของโมเดล}
ในการทดสอบความถูกต้องของโมเดลเป็นการทดสอบหาค่าความถูกต้องซึ่งชุดข้อมูลที่ใช้ในการเทรนมีทั้งหมด 460 แถว จะทดสอบเทรนทั้งหมด 5 ครั้ง ต่อหนึ่งค่า Learning Rate ซึ่งในการทดสอบเทรนแต่ละครั้งจะเทรนทั้งหมด 1000 รอบ โดยจะปรับค่า Learning Rate เป็น 0.0001, 0.001, 0.01 และจะทดสอบปรับเลเยอร์ ( Layer ) เป็น 2 ชั้นเปรียบเทียบกับเลเยอร์ (Layer) 3 ชั้น เพื่อดูผลลัพธ์ค่าความถูกต้องและค่าความผิดพลาดของโมเดลที่ได้ สาเหตุที่ต้องเทรน 5 ครั้งต่อหนึ่งค่า Learning Rate เพราะข้อมูลที่ได้มาเป็นข้อมูลสุ่มทำให้ค่าความถูกต้องที่ได้มีค่าไม่แน่นอนเมื่อเทรนครบ 5 ครั้ง จะนำค่าผลลัพธ์ที่ได้มาหาค่าเฉลี่ยความถูกต้องของโมเดล
\begin{table}[H]
\caption{ผลลัพธ์การทดสอบเทรนโมเดล 5 ครั้งต่อหนึ่งค่า Learning Rate }
\centering
\label{tab:test22}
\begin{tabular}{| p{2.2cm} | p{2.2cm} | p{1cm} | p{1cm} | p{1cm} | p{1cm} | p{1cm} | p{1cm} |}
\hline
\multicolumn{1}{|c|}{ จำนวน Layer} & \multicolumn{1}{c|}{ Learning Rate } & \multicolumn{1}{c|}{ ครั้งที่ 1 } & \multicolumn{1}{c|}{ ครั้งที่ 2 } & \multicolumn{1}{c|}{ ครั้งที่ 3 } & \multicolumn{1}{c|}{ ครั้งที่ 4 } & \multicolumn{1}{c|}{ ครั้งที่ 5 } & \multicolumn{1}{c|}{ ค่าเฉลี่ย } \\ \hline
\setstretch{1.0}\centering { 2 }
& \setstretch{1.0}{0.0001}
& \setstretch{1.0}{0.294}
& \setstretch{1.0}{0.303}
& \setstretch{1.0}{0.286}
& \setstretch{1.0}{0.301}
& \setstretch{1.0}{0.303}
& \setstretch{1.0}{0.2974} \\ \cline{2-8}
& \setstretch{1.0}{0.001}
& \setstretch{1.0}{0.485}
& \setstretch{1.0}{0.517}
& \setstretch{1.0}{0.498}
& \setstretch{1.0}{0.513}
& \setstretch{1.0}{0.513}
& \setstretch{1.0}{0.5052} \\ \cline{2-8}
& \setstretch{1.0}{0.01}
& \setstretch{1.0}{0.699}
& \setstretch{1.0}{0.719}
& \setstretch{1.0}{0.712}
& \setstretch{1.0}{0.706}
& \setstretch{1.0}{0.732}
& \setstretch{1.0}{0.7136} \\ \cline{1-8}
\setstretch{1.0}\centering{ 3 }
& \setstretch{1.0}{0.0001}
& \setstretch{1.0}{0.255}
& \setstretch{1.0}{0.271}
& \setstretch{1.0}{0.275}
& \setstretch{1.0}{0.260}
& \setstretch{1.0}{0.264}
& \setstretch{1.0}{0.265} \\ \cline{2-8}
& \setstretch{1.0}{0.001}
& \setstretch{1.0}{0.613}
& \setstretch{1.0}{0.593}
& \setstretch{1.0}{0.623}
& \setstretch{1.0}{0.535}
& \setstretch{1.0}{0.558}
& \setstretch{1.0}{0.5844} \\ \cline{2-8}
& \setstretch{1.0}{0.01}
& \setstretch{1.0}{0.812}
& \setstretch{1.0}{0.827}
& \setstretch{1.0}{0.825}
& \setstretch{1.0}{0.831}
& \setstretch{1.0}{0.855}
& \setstretch{1.0}{0.83} \\ \hline
\end{tabular}
\end{table}
\begin{table}[H]
\caption{ ผลการทดสอบค่าความถูกต้องและค่าความผิดพลาดของโมเดล}
\centering
\label{tab:test23}
\begin{tabular}{| p{3cm} | p{2cm} | p{3cm} | p{3cm} |}
\hline
\multicolumn{1}{|c|}{ จำนวน Layer} & \multicolumn{1}{c|}{ Learning Rate } & \multicolumn{1}{c|}{ ความถูกต้อง } & \multicolumn{1}{c|}{ ความผิดพลาด } \\ \hline
\setstretch{1.0}\centering { 2 }
& \setstretch{1.0}{0.0001}
&\setstretch{1.0}{0.2974}
& \setstretch{1.0}{0.7026} \\ \cline{2-4}
& \setstretch{1.0}{0.001}
&\setstretch{1.0}{0.5052}
& \setstretch{1.0}{0.4948} \\ \cline{2-4}
& \setstretch{1.0}{0.01}
&\setstretch{1.0}{0.7136}
& \setstretch{1.0}{0.2864} \\ \cline{1-4}
\setstretch{1.0}\centering{ 3 }
& \setstretch{1.0}{0.0001}
&\setstretch{1.0}{0.265}
& \setstretch{1.0}{0.735} \\ \cline{2-4}
& \setstretch{1.0}{0.001}
& \setstretch{1.0}{0.5844}
& \setstretch{1.0}{0.4156} \\ \cline{2-4}
& \setstretch{1.0}\underline{0.01}
& \setstretch{1.0}\underline{0.83}
& \setstretch{1.0}\underline{0.17} \\ \hline
\end{tabular}
\end{table}
\begin{figure}[H]
\centering
\includegraphics[width=14cm]{Figures/5/train}
\caption{กราฟเปรียบเทียบค่า LearningRate}
\label{Fig:5-LearningRate}
\end{figure}
สรุปผลจากการทดสอบปรับค่า Learning Rate เป็น 0.0001, 0.001, 0.01 และได้ปรับเลเยอร์เป็น 2 ชั้นเปรียบเทียบกับเลเยอร์ 3 ชั้น ผลลัพธที่ได้จากการทดสอบปรับค่า Learning Rate และปรับเลเยอร์ ซึ่งค่า Learning Rate ที่ให้ความถูกต้องดีที่สุดคือ 0.01 โดยใช้เลเยอร์ 3 ชั้น ค่าความถูกต้องที่ได้คือ 0.83 และมีค่าความผิดพลาดคือ 0.17
\ No newline at end of file
\chapter{สรุปและข้อเสนอแนะ}
การดำเนินโครงงานเพื่อพัฒนาระบจองคิวร้านเสริมสวยนี้ พบว่าระบบสามารถทำงานได้ตามที่วิเคราะห์และออกแบบไว้ แต่ก็พบปัญหาและอุปสรรคระหว่างการพัฒนา ในบทนี้ผู้พัฒนาจึงขอสรุปความสามารถของระบบชี้แจงปัญหาและอุปสรรค พร้อมเสนอแนวทางในการพัฒนาระบบแนะนำสถานที่ท่องเที่ยวในจังหวัดอุบลราชธานีต่อ ตามลำดับ
\section{สรุปความสามารถของระบบ}
โดยแบ่งความสามารถของระบบตามประเภทของผู้ใช้งานดังนี้
\begin{enumerate}
\item เจ้าของร้าน
\begin{itemize}
\item สามารถลงทะเบียนเข้าสู่ระบบด้วย Email ได้
\item สามารถดูคิวที่ผู้ใช้บริการได้ทำการจองคิวไว้
\item สามารถจัดการคิวได้
\item สามารถ post ภาพผลงานทั้งหมดของร้านได้
\item สามารถเพิ่ม แก้ไข และลบรายการให้บริการประจำร้านได้
\item สามารถเพิ่ม แก้ไข และลบ ข้อมูลร้านได้
\item สามารถเพิ่ม แก้ไข และลบข้อมูลตำแหน่งร้านได้
\end{itemize}
\item ช่างประจำร้าน
\begin{itemize}
\item ลงทะเบียนใช้ web ด้วย Email ได้
\item สามารถดูตารางการทำงานของตนเองได้
\item สามารถ post ภาพผลงานของตัวเองได้
\item สามารถแก้ไขข้อมูลส่วนตัวได้
\end{itemize}
\item ผู้ใช้บริการ
\begin{itemize}
\item สามารถลงทะเบียนเข้าสู่ระบบด้วย Email ได้
\item สามารถค้นหาร้านเสริมสวยได้
\item สามารถจองคิวของร้านเสริมสวยได้
\item สามารถดูคิวว่างของร้านเสริมสวยได้
\item สามารถดูข้อมูลต่างๆของร้านเสริมสวยได้
\item สามารถดูตำแหน่งของทางร้านได้
\item สามารถดูผลงานของร้านได้
\item สามารถเขียนรีวิว ติชม ได้
\end{itemize}
\end{enumerate}
\section{ปัญหาและอุปสรรคในการพัฒนา}
\begin{enumerate}
\item ข้อมูลชุดฝึกหัดและชุดทดสอบมีจำนวนที่น้อย ทำให้ความถูกต้องของการทำนายมีความแม่นยำน้อย
แนวทางการแก้ไข : เพิ่มชุดข้อมูลที่มีความสัมพันธ์กันเพื่อให้ผลการทำทายมีความแม่นยำมากยิ่งขึ้น
\item ผู้พัฒนาได้เขียน server สำหรับทำการ train ข้อมูล และเป็นที่สำหรับเก็บข้อมูลแม่แบบ (model) ที่ได้จากการเทรนข้อมูลออกมาเป็นไฟล์นามสกุล json ให้ฝั่ง client เรียกใช้งานต่อได้ แต่ปัญหาที่พบคือ Angular จะบล็อกการร้องขอเรียกใช้งานจากที่อื่น (cross origin request blocked) จึงทำให้ไม่สามารถเรียกข้อมูลมาใช้งานทางฝั่ง Client ได้ \\
แนวทางการแก้ไข : เขียน server ขึ้นมาเพื่อให้ทำการ train ข้อมูลและทำนาย (predict) ข้อมูลให้เสร็จสิ้นทางฝั่ง server แล้วฝั่ง client มีหน้าที่ในการรอรับข้อมูลอย่างเดียว
\item การบันทึกรูปภาพจำนวนมากที่มีขนาดใหญ่เข้าไปในระบบพร้อมกันระบบจะไม่สามารถ บันทึกข้อมูลได้ \\
แนวทางการแก้ไข : ขยายการรับข้อมูลของKuzzleหรือลดขนาดไฟล์ก่อนจะส่งไปKuzzle
\end{enumerate}
\section{แนวทางการพัฒนาต่อ}
\begin{enumerate}
\item เพิ่มส่วนติดต่อกับสังคมออนไลน์ เช่น Facebook Twitter และ Instagram
\item เพิ่มในส่วนของแผนที่การนำทางไปยังสถานที่ที่ต้องการ
\item เพิ่มในส่วนของผู้ใช้งานให้สามารถติดตาม (follow) ผู้ช้งานคนอื่นได้
\item เพิ่มชุดข้อมูลและความหลากหลายของชุดข้อมูล
\item เพิ่มประสิทธิภาพการทำงานของระบบให้ดีขึ้น
\item เพิ่มในส่วนของฟังก์ชันแจ้งแก้ไขให้ผู้ดูแลระบบสามารถแก้ไขข้อมูลสถานที่ได้สะดวกขึ้น
\end{enumerate}
% Default language option is [thaithesis]. The other possible option is [engthesis].
% Default format option is [ugrad]. Other possible options are [master,doctor].
\documentclass[a4paper]{ubu}
%\documentclass[a4paper,engthesis,master]{chula}
% List all required packages here
\usepackage{float} % for floating figures
\usepackage{amsmath} % for math related environment
\usepackage{amsfonts} % fonts for math
\usepackage{listings} % for psuedocode, lines of codes
\usepackage{caption}
\usepackage[labelformat=simple]{subcaption}
\renewcommand\thesubfigure{(\alph{subfigure})} % for (a) in subcaption
\usepackage{enumitem}
\setlist{nolistsep,topsep=0pt}
\usepackage{graphicx}
\usepackage{wrapfig} % for figures wrap inside text paragraph
\usepackage{pdfpages} % for including pdf pages
\usepackage{verbatim} % for block comment
\usepackage{multicol}
\usepackage{minted}
\usepackage{standalone} % for proposal text inclusion
\usepackage{pgfgantt} % for gantt chart
\usepackage{titlesec}
\titlespacing{\section}{0pt}{\parskip}{-\parskip}
\usepackage{array}
\usepackage{xcolor,colortbl}
\usepackage{pdflscape}
\usepackage{tabularx}
\usepackage{makecell}
\usepackage{rotating}
\newcommand*\rot{\rotatebox{90}}
% listings format
\lstset{
basicstyle=\ttfamily,
numbers=left,
numberstyle=\small,
breaklines=true,
xleftmargin=.1\linewidth,
frame=single,
columns=fullflexible,
captionpos=b,
showstringspaces=false
}
\usepackage{tikz}
\usetikzlibrary{shapes,positioning,arrows}
%%------------------------------------------------------------
%%- SETTING THESIS PARAMETERS -
%%------------------------------------------------------------
\thesistitle % set the thesis tile (use {\wbr} for word break)
{ระบบจองคิวร้านเสริมสวย} % Thai Title
{Beauty salon queue reservation system} % English title (auto upper case)
\authortitle % set the title of the author, e.g., Mr., Miss, Dr. etc.
{นางสาว } % Thai Title of the author
{Miss.} % English Title of the author
\thesisauthor % set the author name (must not include title (Mr., Miss, Dr., etc.)
{ปิยพร อาภรศรี} % Author name in Thai
{Piyaphorn Arphornsri} % Author name in English
\advisor % set the advisor
{อาจารย์ ดร.ทศพร จูฉิม} % Advisor name in Thai
{ดร.ทศพร จูฉิม} % Advisor name in Thai with abbrev. title
{Tossaporn Joochim, Ph.D. } % Advisor name in English
{Tossaporn Joochim, Ph.D. } % Advisor name in English with abbrev. title
% (Capital letters except Ph.D.))
%% If the thesis has co-advisor, use this optional command
%% Uncomment if necessary
%\coadvisor
%{อาจารย์ ดร.ภควรรณ ปักษี} % Co-Advisor name in Thai
%{อ. ดร.ภควรรณ ปักษี} % Co-Advisor name in Thai with abbrev. title
%{Pakawan Pucksee, Ph.D.} % Co-Advisor name in English
%{PAKAWAN PUCKSEE, Ph.D.} % Co-Advisor name in English with abbrev. title
% (Capital letters except Ph.D.))
\faculty % set the faculty
{วิทยาศาสตร์} % name of the faculty in Thai
{Science} % name of the faculty in English
\department % set the department
{คณิตศาสตร์ สถิติ และคอมพิวเตอร์} % name of the department in Thai
{Mathematics Statistics and Computer} % name of the department in English
\fieldofstudy % set the field of study (สาขาวิชา)
{วิทยาการคอมพิวเตอร์} % Field in Thai
{Computer Science} % Field in English
\degree % set the degree name
{วิทยาศาสตรบัณฑิต} % Degree name in Thai
{Bachelor of Science} % Degree name in English
\academicyear{2562} % Academic Year (in Thai Calendar)
\authorid{59110440259} % ID of the author
\subjID{1104494} % Course ID for undergrad report
\subjName % course name
{โครงงานคอมพิวเตอร์ 2}
{Computer Project II}
\deanname % name of the dean
{ผศ.ดร. สุพจน์ สีบุคร} % in Thai
{Supot Seebut, Ph.D. Asst.Prof } % in English
% Committee Block
% Speficy the chair/committee/external committee in your selected language
\committee % list of committee
{
\CommitteeBlockAdvisor % pre-defined value for advisor
\CommitteeBlockCoAdvisor % pre-defined value for co-advisor
\CommitteeBlock{กรรมการ}{ดร.วราวุฒิ ผ้าเจริญ} % examiner
\CommitteeBlock{กรรมการ}{ดร.ไพชยนต์ คงไชย} % examiner
}
%%-------------------------------------------------------------------------------
%%- DOCUMENT -
%%-------------------------------------------------------------------------------
\begin{document}
% Thesis starts with Thai Cover
\makethaicover
\newpage
% Followed by English Cover
%\makeenglishcover
%\newpage
% Generate committee page
\makecommittee
\newpage
% Acknowledgement Section
\include{acknowledgement}
\newpage
% Thai Abstract Section
\include{abstractthai}
\newpage
% English Abstract Section
\include{abstractenglish}
\newpage
% Table of Content
\tableofcontents % generate table of content
\newpage
\listoftables % generate list of tables
\newpage
\listoffigures % generate list of figures
\newpage
% Main Content
\include{1-introduction}
\include{2-background}
\include{3-design}
\include{4-implementation}
\include{5-testing}
\include{6-conclusion}
\ULforem %%%
\setlength{\bibhang}{1.5cm}
% \bibliographystyle{chulanat}
\bibliographystyle{chulanat}
\bibliography{references} % speficy your bibtex file here (this example is MathCS.bib).
\normalem
% Appendix Section
\numappendices{3} % the number of appendices
\startappendix
\include{appendix1-setup}
\include{appendix2-installation}
\include{appendix3-manual}
%% Biography Section
\include{biography} % specify your biography file here (this example is biography.tex).
\end{document}
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" version="8.5.12" editor="www.draw.io" type="device"><diagram name="Page-1" id="9f46799a-70d6-7492-0946-bef42562c5a5">7V1Zb9s4F/01BjIPKbRbfszSTh86QNHMN8tTQFu0raksGhITx/Prv0uJ1EbKVmItHoBFgFoUTUs8h+TlubzkzH7Yvf2aoP32NxLgaGYZwdvMfpxZlulYHvzHUo55iuf7ecImCQOeqUx4Cv/FPNHgqS9hgNNaRkpIRMN9PXFF4hivaC0NJQk51LOtSVT/1T3aYCnhaYUiOfXPMKBbnmoaRnnjKw43W/7TvstvLNHq5yYhLzH/vZllr7N/+e0dEmXx/OkWBeRQSbI/z+yHhBCaf9q9PeCI1a2otvx7X1ruFs+d4Jh2+cLcX3im6S7WhocXPvZvzXlexCuKXnhlPMXksCRH/sD0KCopPYS7CMVwdb+luwgSTfi4JjF94pnYNYrCTQyfV/BEOIGEV5zQEKr6jt+gZA+pq20YBd/Qkbyw504p1KO4ut+SJPwXikXiN+B2QjlrgGrVHE/sm5BsQGqCU8jzXVSGWSR9QynleVYkitA+DZfFA+9Qsgnje0Ip2fFM4k2/hFH0QCKSZBUgkIVSGeA4ELkFpnn5u3DFP0doiaP7giGipJhkdZjShPzEleKN7F9xRxAxq+TKg/Cvs3r/gnZhxJrcHzgJUIwEHHlNmRa/Vv2GTBzOJQYXfqskcSL9iskO0wRoYYi7nNNHcfnJzRMOlSbk8UzbSusBGvKmy5vtpii7pC584OztyOTiBUomz6x71huEmHUHd6xTOe6xxGt4XVrndB0aUeEyBoLrEV7TVqane7QK4823LM+jU6b84DXCkg7bkOInSGfPdIBOFtIIlLeOMmZtwyDAccY8iijKyctg3JMwplktuvfwB5X9wGBw4b0e4Nosr+GPZU+ADTG8Hwoz3DG0jANmrUPBiPbO4jxHOCnEwHCWEt4QjLA1I66OEa41JSNczYirY8Tcn5IRnmbE1THCNJwpKeFoSlwfJWxjQkqYCwUlGuhHYXNq1MmOPwP9DgCLcIn174wKj7emxAdb5oOtwDmbk3wnaUhDwspP8rwN/KeB2O1oLPpDNHqjpdFD0VsS3LAG/4tu+9MQw5t0OJAVEs2Ma2HGYtJRQTEK4GCDhRyGoyU5fC4Tqhw4pw2hhDL1ieNQFcB8uMZxcMdkT7gk+ww5SKnkhyshAnndtaLseeFCEA2SApRuM6HLPKUZpeQlWeGWWhIDN7zCBrdB7/CpOqu+k8AnOEI0fK3rtr2iassjwVdCDyQJHjHFKwo4aWVUK6OnlFHLrkuji87K6HwIZdTWyujQQ1fRafw3lFHb0oy4OkZMqozaWiu/PkZMqozaWga7PkZMq4zaWiy/QkpMqozabeqIpsSElOiqpA5DCV9BCS2W9wyx39FaHEIst1XuEC2JXkfbn1QSdbQb5WqZATSYkhltkoNmxvTMcDoOJr0wQ4K26jOBV9+SDYlRVHWc1GXlCgfwW0j/YslQffnV3+JODM9VucUu/+YF/IMpPXItGL1QwmAsfvcbyawGoeAXSvJXHL1ixhwlCl08IcLJcdIT4vGxtT9PSPbVuyRBx0oGTsmy5O8soZSwXbH0nVPENhoLyxv5Fyezw4f8AUqGFG/SiTSySZniOICUABpah74i83a0tsjHWQfvTGFctjgvqm6QdxCnzedZBDvwp55VAwZUTodb45Mj2jFHweFiwfuoImHrN7C16wWQ9ToFOje7gPcB3NpYKpA/kN0OxcEPvMLwMtoNp91wp91wjlUnbnc3nFgn3K8FpAXVoU2fotP4b7jhHL0c/foYMakbzmlTVjQjpmPEpG44t01R0YyYjhHTuuFc1XoeLbD3DHFXt9oQArsYg7RYdoVt3550OGhbp6OZMT0zJo1TcOXFGY9QfQ8RRrEWS7RYckYssZ2Gyiei5ytkLpTAKpmF1Nszm+V1Jbdw+fNld0TLNO/gojCluoP7cAdXdBiXiCVKSgzTv8k+gFtOgmcWnaFJMQ0pFHrJeKTwVJqqngr1i7BC/1AiPMRMyGuTSBO8j6AtZU3/jpL7G6gPqGLWKBj6zc/cJhY3dP8wGnv8KQcNr23FcpU+N5oi01LENCcdQ1TrVXOO7KB2n1NAKDMwbphtwViibYyxCaLQXAYjSKvEwkjwTAHSGKZkmgyTkUEhswxHhjan/ZIcaiZHToOH7A6JGDH4kivNjPGYoVj7PBwz2kyLVYTSNFwfa/SAqkW6u5hEtFAsex6OFO3TlXQPb41rpGA9Rb3P0Mbn+PxQLH4e0fiUF3zsExyEK7mT0Iq9VuxP7L+sEuyV3ifTGEKomasCQbUSd6qvKtp+76sVBwFYrb9vML3RI9ckbLBHdDfL0yDAImLHH0iQV7BugrbkvXx1A7NlRNgIUgnYmVXDdbLondZ4ChH/I2KByq+0drAnQ3SK0ydOheiI/ZR6DtGR4i7seXOH/UW9iPxV+LdKLOWCpK36GwXlrysV9IE4Dtn+TTB9SeJLWcK3uavtR1fucudXqGBIVGhnj6CcKVPuY/wRTfIkf0R7HJo/TiOGy/KMTrDL/HHOFNRCxPagswsYZsqrH2FuHTzv8lNptNGsjebuRrPrdl3MJ2aJlwyiws3RMKGyGMbM96Sc92kTqsWEMrujP0G0jym7EW65E0EvVRkF7TEjeUzZQGZoZ6/2/KbxHgPvMeN0TNnQLfE+arzHwHvUKBxTdvcwwOHFcFYnevgeAfAxt7pTnIzIIy2yiUYeYrGOMFJMbjXm/WE+5l52+pyXAQAccac6xYl/+YisxQEtDpwXB7yG4Oo6ig1DFgrq2kYP6oBCY9cWxkWjjdXLdiAqwPsYbBRauQ5oGR9ylUowGOTqSYRGezS0VRrBYGirYxiz9YFY4z0K3kqNYDDA1UskArJ62ZVjuIZ8YMhVKsFgkMszxkIHZOthNOJjIK7SCIZCvCUOWatCoyKuEhWGQlxxIiBDHKb2eza71y18hHmZMaLdpjwvT6uAlwGoWqyoArAHFVBxuh1rr63hnLr9Dg+/aof5wdqvWkhTh29q8EcQ1cbsvGVV7WmP8WorIazVf63+19T/Dqf2WoaCtqbYo/XE0twfbCf/eAMolUcmNLYnt+UusksYGoqAlzGi+J6Bcunq2tYj/lZQOjd2xSpf3V126C6L/uiiXbZUtOslrEO114E2dS/CT7Uhlgq/Hizd1iMF0mzIo4Q1y5tXEgZ6c5uRWq8ni4/Dtd62aHOGJyU5CcqI89o2R4wUmgfD8UC17ZWSB310A22D9j5CxxsN+4iwm6ascwzX/mXnw/9Sec9nOYyujJzLjOpaVF1jLnQ6cE4Rq9l3SKbo5KohdWLcOxtBp1pzdmFU3dytm+3mwqkX0TUq02+cq2X6jYO1eozKnIwoHc7nkyn0MaKIAOoqUdwpidJYTTa3jI8RZWGfKaiFKO89869xhp/Lu6i+zvDzZDfK2CS0TpwSaUnHRCqp0w8vzfFo2Ih4dPz5x1hozs8U1F935ckOmHGZUh+75u6s/TxR88NEEUupT8aK557tUYhi+4tPi7nh+j7YPdbCEZaM6HUM61OWbM49B7J9dC+C07/iM9mtuGfO/U4ce29P5zSG3rlz0XGlcJkQQqvZwYDd/pYvCfj8fw==</diagram></mxfile>
\ No newline at end of file
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" version="8.5.13" editor="www.draw.io" type="device"><diagram id="813d08ba-7be8-3800-d292-465b29ec56fa" name="Page-1">7Z3Pc5s4FMf/mhzDoN/iuM1ut4ftTGdy2PbI2iRm1jEeQhpn//oFG2xLEExc9IRk+9DGMpbgvY+kp68e+IbcPW3+zOP14ms2T5Y3OJxvbsjvNxgjikn5X1XytisRDO8KHvN0Xh90KLhP/0vqwrAufUnnybNyYJFlyyJdq4WzbLVKZoVSFud59qoe9pAt1VbX8WPSKrifxct26d/pvFjsSiUWh/IvSfq4aFpGPNp98k88+/cxz15WdXs3mDxsX7uPn+KmrvpCnxfxPHs9KiJ/3JC7PMuK3V9Pm7tkWdm2Mdvue5/f+XR/3nmyKoZ8oT7tn/Hypb70VX1ixVtjjPIbpd3LN59eF2mR3K/jWfXJa+n6smxRPC3Ld6j8M35e75zxkG6SsoFP+6sLyzcP2ar4HD+ly4qJL8nyZ1Kks7j8oD6FJC+SzbuXgfbGKaFLsqekyN/KQ+ovyNqcNW6kMe/rwXm0Lloc+a0pi2tcHvcVH0xW/lFb7R2T85YJXbSgbkIJaEHhpQVBIZRemnBvGwgTtodCH0yIOJwJG+A9MyENAU2I/DQh4HTSWMwzE3LAsbAJ3z0zIYMcC6kXJqSRtbAQMy8tCBkWYj/WJroJIcNC7MfiRDchaFjox+JENyFoWOjH4qRlQsDphPixONFNCBkWEj8WJ7oJIcNCOoTC1fy3SsMu362yVaKa7V3DJJu0+F4fVP39o7JkwKp3q/Ikv9eG3b75UR+3azqZt8RwzZTl6WUv+aw+qh6Lijh/TJqjeLfFjyzKOizalOXJMi7Sn+pZdJm5buFblpbnt3fofjJrQqyQqVXszr7+1sFZrYr2+w9NoMG0inbX3Kpo6/X9ZQ8DYciIfh4Ine7eoTDc4Yp3I6vexZp39X442LsRC0R09GJKvahZso3vbGau14/vbOmJs8MAh0cvzdlRaMzZQ2bJqThbXIKzsTDnbGzD2e/P9cMxODGjH+1G2yCDaGQwM2S06h2RjCEaqpNkvBNdg5HxIYeOBAol5kAZohSPCQoQJsQqJqgfE34uJqK3XobNYTJEDncQE+znaNK71GCCG8NkiOQPIjD8CihNSpBCCrVJCsElKZIJROt/VVAI7/r0o9hQyZVqETI3nAzZ15h8eNLJidU1DdHXNETr6sNZ6AMOCQ04jbgRQRmye+MmKFZFSyBQygmhb2QakRMrgiYIJ+wCODkxg43HSRN5e8iJVeUUKEDpa4QiKIasSKwgDFndagEaa2gExQn2lRNiVYQjujwrz1wnt7fizbHghR7byYJVqd7KwpgbHDPMybGQAgrvmFssCygsYKRaT4acMtRM4c16lpAg4kJQFBFGJYnO44SFKCBEirCpS2nkNkKBPP5Ua2VEiKDFWjODTRdEltUV+xAhDSIpjVFkTsu1TZFl6QWAIhr1NcJ62xgRIT9k3i6ELKsy1hE60ciIDPmhAHcxZFmxsc4QjqAY8kMd7mDI8krcfkBEWBTsqq+PMMWQ8EM57mLI8greOkOUQDHkh3LcNZfZVY77E2laiQzDsbGVliewMVLGpsGu51HZdY9fVPU8Lz8WkkaUY8IZOzuM0e5fDc053pxQDCkAduXa2dVurICC9UzPEUGBTtw1M5d0gWJ3bW0FFIJ7qx0RGz+04S5s7C6nrWBDQyhs/BCDu7C5wPiFSrVaY3sIwg8BuAsbu3sIQNiwQPIQIxYyIjghaiucMHUvyhhFfkjAHRRZll6mQJFAUBSZF4FvwyBEQkOpHF1PLr6+JXlaXkqS1yiOSJhdgdjK9MY40PTWJJ65tlifKA6YaTiQcXg4Ve+IQDgq8F4YEIQALZskvvLgAA8UQfHgaBrwpfEQQfHgqIh7YTwwARVQOqvOOrPTMx4j/ctaDLSslc4qsxO6O2ASyMgQChlHVVnv8VBnHc6AZp3IHA8eyxjb+0w5DhFTs4I4D4Ow7MYs4rI66synwW73aM5tZEQ2nNXepza9WOVFUChenE21nloEa5UXiYF42afSTTHT0W8A4HLVUOioLj69xDSro0IkwUYF7AUwE8gtsgoMQlBxBwodldant11vlxgMF3mYE98Np9pPLe3VODHa9oy55xyj0JwCf4FaSLVxwkwPGScaGRMOL7T2KSQdmgbm1O+vBAYhuarrzgERGQXCnIR6dfoJp6vdXhh1s6PKp+8I9P42EzNJBDInbV6JMEQENRobIEd10IsmwmhwgDB8cHBdb/4aEVX6uEEirAiZk/e79nDwjnH6XDWBtCqG0R/RNfl3xK5/wm8f6fqIH15NbtTRZCDJ4WWQDi9SgaesNY1JzFFyoFQaueXIHCLO/tqaK1te4yGiVNvcqGuCiav6OEn/9wwRkUEaHM3evGAasLm7zRC6SpSu4UDMPRUZYXP65AVqDyA4IGEQB3OjwyXiEAGkPpxoZEQ4iIWx4grATcddYQbTtImj2xMOLShHgyLY/6hZ9a86SUQBOfrRM2luyiD4OipMYlQwukdFHE2t9p2AviFAqGOAwSHguovhGhzV/iUQHI5uYlwwHBSBweHo9sUFw1HlywHB4eg+hvpEVrs35zDav+00lAmB1HpMapLE0R0Lze9Wb/zU/X7bqmOSnnd0c0L1vF3xoeV5RB3wPDWnLUJ63mqqS8vzHAWHJ41RGoqxOJAwD6Xak+taeKhRQaxSQdSQket9WtmxagLyDzMS9gWmp1oZExnsw0Bi96dF+pHBTCqrjLORgZxeHJUvNSrodKmI5DjjCCQU5mTLsR1v9Vlk/Y5HwrnhoGMzY9X2/HKZrp8rj78u0iK5X8dbb7zm8VqlIH5eJ7PK4g/pJilb+PS8iOdbYsI+RupTSPIi2fSz0Pb7eyZrsnmOuKAdXDRlXQgoZu3tPY4+MVTrWXWgomweW47XwgCFZbgusWCSNxUcRVICU0RERLZHndnZwr77X/RwDWutjDkGt7Wev+K3JC+LGm8dcMqzl9W86mJb/5/olmC9kLY73T6t7pgPPWnjvF7XVkgae7W73zTtBWsw1hYWGoMRRwxGGajB2mvuxmDUEYNxac5g5ds8y4rjEa+80MXXbJ5UR/wP</diagram></mxfile>
\ No newline at end of file
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" version="8.5.12" editor="www.draw.io" type="device"><diagram name="Page-1" id="13e1069c-82ec-6db2-03f1-153e76fe0fe0">7Vvdc5s4EP9rmMk9XAYkwPDoj7R56M10mpu566NiFJupjDxYjpP760+yJT4kEWMMTtO6eSishCR2f7/V7go7cLp6+Zyj9fIvmmDiADd5ceDMAcDjf/w/IXk9SIIoOAgWeZrITqXgIf0PS6Erpds0wZtaR0YpYem6LpzTLMNzVpOhPKe7ercnSuqzrtECG4KHOSKm9J80YUsp9Vy3bLjH6WIpp44C2fCI5j8WOd1mcj4HwKf9v0PzCqmxZP/NEiV0VxHBOwdOc0rZ4Wr1MsVE6Fap7fDcp4bWYt05zlibB9Q6nhHZyncnlK7l4tirUghf51pcblfkU45W/HKyW6YMP6zRXMh3HAdctmQrwu88fvlEM/YJrVIiIHCPyTNm6RzxhuKVXX5jrle+wjPOGX6piOT6P2O6wix/5V1UqzKKwpq83ZWGg6rLsmIzT3VEEiyLYuhSYfxC6syuv5Ghvu0G5w4ICZ9l8iiuFqx4T7tCv6RPmKSZ0ClXBNdSukZ7bfC28ZzRXDTgPOWL40PDGZH9v5ayY8bgPGGIP5IX94Sg9SZ93C9HGCLH822+SZ/xN7w50FFIhRW41ciYpIuMyxgVA2/4PGm2+FvczGDIJQQ9YjIpsD+lRKx6VqIfTuiWiUVPC8a6b4GkB1wEgYEDYIFBAHqAATRgYBi8Yo01TTO2ny2YOMFMMy/N2ZIuaIZI1cCNmkpSbjqWUmGeHd6wfpQX1jkVmZzyLLr03R506ZkuyYHje8p2NE9mXB17SrTk0/vQ5uJY94DNQEOh3fNsFprS1QplyTc85+q4GqhuIE6NSxoI2Ay0znGS8re9WqZqGd/q2wazjGWngOMZYmhKMMquvDH2oYtaJ7BZ52GN8Xx5NUzNMDC8qGHMOPuDB1heGHSJsKI+lBn9asoEUV2Z9nDI1CYY9aHO2FTnnetMIiee7C9iJ4qlZDxTTbFYyiGmfUv5egL2SBmjK96As2QsCh1CRuj8Rye646RWBjH1W9GfQl6OCWLc/dQGsulPjvZVwKc01aieWMTxbVAfYkO3+RzLp6qVC20gL4R1m+u5B0P5AjNjoL05i1dsZWFgxrvcjXNrAfdemdAVctHkPiGywYZNOZJZ3dXnwn+j0rGbPENbRqWPFw8giQGCn1i73PxPf+i8Y1Qnnm1T8C3E83rgnSXpTjdTvl9yByPNcqP45yr+jeXFJFASRdZo9EeLss2vTE1vBG81Pwo707NY1KsGgwHoGTY5YGluDoCosLKBBE81zfSLSeHIobq4U4/DRtc+9up9KlPEatLIV02wYT31NZ8JQ7rGmdh80WaJE/kcb5Z19+gtgHJc5q//Cl/i3nqepyTfhdO6HQWFoLL7v13YPdjdqZbNjmL9TGAb4YEeRbUGtXtkoB5BbYl420QV74KvD+XmtAgE6PlJVw9nLKVHMFgi9qMe7kQMnLxX/kaQAXHQD2Z893KYsaQlPWd5tZO8QWt0mt8F1qqQM0i2DC3HETrBRmf63xt1BRpdezHiWE1WRCdj72w2nhEgdOfpmaQ0uBR2deQ6u/WB+iMltJycfFRSBkBT2wVLWBA0bojNgXm5M4JGdhV8jQO9Tzy+6cbu9yb3h95qoV7Wacvqou7cNFCPrB7++P9irA79bqzu42Ma6Dey+o0wtyOtfitWBbAnVoX+5VjVUNbhli7qKRNlzqAOi7ZpjN65zK3NkK4vnAnzP8jbkux3pbRaJNZqvrK6+2V/NwN2RO4/wdqHcW61gFOv3fycIZ3XLc/iGkCvlW7SwzbP49trACVYDyM2PK3tLGC40iZsrgIVUIOtUKiTwdVBXBY7/SviL4b4MOjmmE9FfBjZ/XY7xIfa54BQ52V/iPc7pdtdkapzKdDDjMFpVtnNxoHxFu9Z+X8/TmghqIG21imAHqwMCNvGhNREUAHbEjgmxt/KQ68O9pTI19cONSEcxsUGQdwwUzsnG0DjeT2p6hGvjanWSVXNVuj8CVxW9dBRff40OPLAEWu2PjTQ/eGAsLB8APlRKxn6oYH9i0VnkEqG3xi3XysZPdcHu57l68cHMBrsKM5vOL5tU8k4M7htU9toXSw5/nnUNRBpXdtoC9xzaxtqnm61DeMXXT2yovG7WYnvio8ss6YTY5EjCV5cjlPmY/E7RjAFot0aom/BR/jOSt9xDfC09c1RNxCeShXg2+exUuU4wvlt+cPtQ/fy1/Hw7n8=</diagram></mxfile>
\ No newline at end of file
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" version="8.5.12" editor="www.draw.io" type="device"><diagram id="4d4f0bbd-8f27-e171-19e5-097d03e0079a" name="Page-1">7VpRk5o6FP41PN4dkoDCI9ht+9KZO7MPt33MSlSmaJwYV72/vkESQgIRV9G6nW4fCicJhHzf+c45iR6aLPdfGF4vvtGMFB70s72HPnkQAvFP/FdaDpUljMLKMGd5Jjtpw0v+P5FGX1q3eUY2RkdOacHztWmc0tWKTLlhw4zRndltRgvzrWs8Jy3DyxQXbet/ecYXlTWCY23/SvL5Qr0ZjOKq5RVPf84Z3a7k+zyIZse/qnmJ1bPkh24WOKO7hgk9e2jCKOXV1XI/IUW5tmrZqnGfHa31vBlZ8bMGBNWIN1xsiZrycWL8oBZDjBDrLm7SBV8WwgbEpZj4umzfcMz4C8e8bJ/lRTGhBWXHgcg//pWdOaM/SaNlNlMt6vvLmxld8c94mRcla76S4o3wfIpFg5wjYZzsnR8K6uUTtCR0STg7iC5ygCKgJCSS67/T6CrTogGssmHJp3n9XL2m4kIuq2OJw/YSP/teGnlxeryIvSiWluSTaorLmVC+oyxrA5IJsspbyviCzukKF8/a2gSqXDWxikVS5POVsL1SzulSNJBVlpSuUj5kTVaVRbpidBK0HpzK6RkobeiWTYlJuDOQY6TAPH8zn9WFhBz6L83FE2vEge8bkANogSmYOydcjrLwrKdxHsRjJ8ShQhapi7G8iCJlSVroJyfQZwu6fN2Kb0h3i5yTlzU+ru1OKLGJvOWNpQpNH8QbkQOahjuCoMMfoyH8MeoGS7hhoIAAyqIwikfyIlWuGoEWMu/ySyz9sSAzfmc3FSCxw3c5j+PNjxLzp7C83ee8bhLXuuUc5x47iCDC99HZmhrgJMc//hMSf9W4a0UAmkwLz9MAseL40Oi2LjtsTrwmtgg9ssJuT/8gtBhcTcAarGZDZ7MNuVqzYqdm3ZvrnK5vTHS/TfTh6NxI3DrpDIZhshXOAv9GVLbeA00qm9y8gHfKIdu8q0NkCnQclARUypwiO2imLa2OWhE2hZrI8skTk+N1DiYu1CuiRDe1gnCZ4pNM8rsnEGM2lTwO/MeOy0q3avCDjsAMOwJzMEBghs4sKkncGF/ADGHxW53rRA3qzh8m8XZH7SslUGHdI4EIdtPqSs1DwKKjXY85NK/3QWBsPaj65iFqAehIL4XmjG2qxrEtUJp90NSlRnWQAFO7hCX8zQI1gPSgsQlRdEflQY6YFDcCRqrXuleCdNXXqOgs5dHF/9hkw8Xq9OEgD4PfCDm8HPKzEToX8pPurqvSWj9qtWjn5X9oYoJC9GTu4N01NVHV6M1TkwGZ8PDJit5w+NHMTxpNKl0B5yUryqf7kpXoNsmKVdMjm3gDFWiB2kJVjuCf3muAVmC1+l9d0KHRIL7RGTM/HKcHScA7+Gvk5FEHpx17EFdyOohM7sQX5t+htQ1XH/QNn3+jd+TfpwXV1mzfJmjdOQ76xfsBs/a7RW9ol18dx2+3i93Ojc5T+nRpFLapFtqvuDkLGxlsEra+4u+mhpaIvn3dG+UJlhYC+1BruHPJoKOubcF/6nS/xPDxz/ZD63BfZTj3ON0PQLe6NGvEtga8s9L/s3Mmsw4AnrsO8N3+3ZMzKaib/h3cJmcKoclGgMLLkqZRYNPaepAjaXpvQTFy/FDCOS/7hOi6gkLc6t87Vd31j8rQ8y8=</diagram></mxfile>
\ No newline at end of file
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" version="8.5.12" editor="www.draw.io" type="device"><diagram id="120ff508-374b-bf88-8e7a-aaa9685ebcdc" name="Page-1">5VlLk+I2EP41PoayJD+PGGayqUqqpjKH7B4F1oBrbcuxxcDk16eFJduSzYZQwLK1cxmp1Xp9/fVDxiGL4vBrTavtHzxluYPd9OCQpYMx8nAA/6Tko5WE2G0FmzpLlVIveM3+YUqo1XZZyhpDUXCei6wyhWtelmwtDBmta7431d54bu5a0Q0bCV7XNB9L/8pSsW2lEQ57+SeWbbZ6ZxTE7ciKrr9uar4r1X4OJm/Hv3a4oHotddFmS1O+H4jIk0MWNeeibRWHBcslthq2dt7zidHu3DUrxTkT1LHfab5j+sTHc4kPjcV+mwn2WtG17O/B3g5JtqLIoYegSZuqtcBbdmCwavLGS/FMiyyXtv/E8ncmsjWFAbUVqwU7nDwu6kAAcjFeMFF/gMp+aAWF3XZggU5IleU33dz+9tBQAJxAD0+gEeSwRbKCxkYcL9EK5C0NnIK/d1wP/NIcGT0HBexVh36wW+XJdZLImS91w1eNBKlGDJZxdTuxR6Ol3UiwrTwneqhbx9MS3ZjHeqjViZ1ooSW6kcR6SCtHcy2J9DqhbnSIAdgtRiZuIB5gaVENiCFMdt2ETGoCJoozKkghv+0OuEY0JYZcI9egGvlBHK8L6SZWsTvCyp/yS/8qfumPwFoyIS8Pm3Gx53U6Ag9WgGQBneQ/YLwlal5gMcwbw4Ymw9k1UItGqM3XgtcjqCAHVbK5K/JWgSTy1oBC/jtdsfyFN5nIeAkqKy4EL0AhlwNJl+wWPJfzln2669eY59lGzhXcQp7vRJ6VbNHlcPfG5vBNa2B/wuEnjBFcwRjayEN/t8N0F/pl3vitrHYyeNJColaummoqYD4Ey0lo4krcO7JcV5oDYF8FhfILajF5xgJq08eEzU4/eAo2bwK28BqwTRR+io+j5B7p5B5huxLoCxhkVgtdSQPK3qgywTbl1RagrGuVKBnVKmRUojzbs/o6ZDkx6yFp4FlRqcsZ96ABmQhLOuaocm3Yy8p1vpP+pIQbQ2W6pmMpvKxUt+QlM5FlZTqX7zXo8oqVE2mjSzkpbbayzjnOO5F/1AYn7cUOmfgsM83MV70v3UEAusGQ7H5ROam9k7zIt20M9+a7es3MkC9ovWFi6HbffN4gbf+a5VRk7+aeU8ZWy73w7Pgk0eEliExe2dG2PauaNXwiWgt5rhXeQ99cqL3gaKEj97o7nkfHcVX8J2sqXjaPGsPR93Re7yd13oHrDrz189CT+7ELnTceO2/Lzrs4L4ksn4ssnzvXee2FvPh2zhv+LzrCU5/JMu1qbGzgKsLi44ChasJRS31whMf93fKNG2ODthGJVP+F1RnAzGpN7btkonuSGfkz36JzOLuQ0JCNZihGgRcGbkgCK8kRL55FLmiFhMSx7TRX5PoZ303Pjp1q8CQRT7OrY8o47GnmIYN1xPo7QcHLaKY/OBgFT3A3msXWc8ouU85lGA7sese9FY28qfL7/jR6EKq4340ql2ZXZGVXjG8WcTz0kxZ7s9gNDWIiPzwZ+9BVWaq/LQ1Z2n6Lvk/etL6rYXJpzrQjGroWTaHb/1bZqvc/CJOnfwE=</diagram></mxfile>
\ No newline at end of file
\section{ }
\subsection{ }
\begin{itemize}
\item
\end{itemize}
\begin{enumerate}
\item
\end{enumerate}
\begin{figure}[h]
\centering
\includegraphics[scale=0.5]{Figures/2/}
\caption{}
\label{Fig:rbg-image}
\end{figure}
\begin{figure}[h]
\includegraphics[width=\columnwidth]{Figures/2/}
\caption{ }
\label{Fig:}
\end{figure}
\begin{figure}[h]
\begin{lstlisting}
\end{lstlisting}
\caption{ }
\label{Fig: }
\end{figure}
\begin{table}[h]
\caption{ตารางขั้นตอนการดำเนินการ}
\begin{tabular}{c}
\includegraphics[width=\columnwidth]{Figures/table/use-case} \\
\end{tabular}
\end{table}
\ No newline at end of file
## Run the following commands in target folder
### Fonts
1. tex-gyre
[https://ctan.org/pkg/tex-gyre?lang=en](https://ctan.org/pkg/tex-gyre?lang=en)
2. th-sarabun-new
[https://www.f0nt.com/release/th-sarabun-new/](https://www.f0nt.com/release/th-sarabun-new/)
### To compile
```
xelatex -interaction=nonstopmode -file-line-error -synctex=1 MathCS-tutorial.tex
```
### To generate bibliography
```
bibtex MathCS-tutorial.aux
```
### To generate .eps file from .pdf file
```
pdf2eps [no_page_in_pdf] [figure_file_name]
```
### For example
```
pdf2eps 1 er_diagram
```
This command will generate "er_diagram.eps" from page no. 1 of "er_diagram.pdf".
### วิธีการรัน
```
Compilation: *require XeTeX
[When citations/references are changed.]
1. xelatex -interaction=nonstopmode -file-line-error -synctex=1 MathCS-tutorial.tex
2. bibtex MathCS-tutorial.aux
3. xelatex -interaction=nonstopmode -file-line-error -synctex=1 MathCS-tutorial.tex
4. xelatex -interaction=nonstopmode -file-line-error -synctex=1 MathCS-tutorial.tex
[When citations/references are not changed.]
1. xelatex -interaction=nonstopmode -file-line-error -synctex=1 MathCS-tutorial.tex
```
\begin{englishabstract}
Beauty is popular and is a business that has been popular with many customers today. Usually when customers want to use the service, will come to the shop without an appointment. The problem is that the shop has customers who use the service at that time, causing those who enter without an appointment to wait in the queue. Or if there are many customers waiting to receive the service, may cause to use the service on another day instead Even in some cases where users call to inquire to reserve a queue in advance But the technician was not comfortable to answer the phone because he was serving other customers Therefore, the developer has the idea to create a salon queue reservation system. To help manage the said problem This system was developed with the React framework, material ui, nodejs and MySql. The system allows customers to reserve a queue. See the salon queue. Find a store location See general information of the store And can write a review In the part of the store owner Can add general information of the shop And can manage the beauty salon queue reservations The queue reservation system supports display on Smart Phon devices and web browsers. The developed system will help facilitate advance appointments. Reduce user waiting And helps to make queue reservations more organized.
\noindent
Keywords: Web Application,Beauty salon queue reservation system
\end{englishabstract}
\begin{thaiabstract}
การเสริมสวยเป็นที่นิยมและเป็นธุรกิจที่ได้รับความนิยมจากลูกค้าเป็นจำนวนมากในปัจจุบัน โดยปกติเมื่อลูกค้าต้องการใช้บริการจะมาที่ร้านโดยไม่ได้นัดหมาย ซึ่งปัญหาคือร้านมีลูกค้าที่ใช้บริการอยู่ในขณะนั้นทำให้ต้องผู้ที่เข้ามาโดยไม่ได้นัดต้องรอคิวหรือถ้ามีลูกค้ากำลังรอรับบริการอยู่เป็นจำนวนมากอาจทำให้ต้องมาใช้บริการในวันอื่นแทน แม้ในบางกรณีที่ลูกค้าโทรมาสอบถามเพื่อทำการจองคิวล่วงหน้า แต่ช่างไม่สะดวกรับโทรศัพท์เนื่องจากกำลังให้บริการลูกค้าคนอื่นอยู่ ดังนั้นผู้พัฒนาจึงมีแนวคิดสร้างเว็บแอปพลิเคชันระบบการจองคิวร้านเสริมสวยขึ้น เพื่อช่วยจัดการปัญหาดังกล่าว ระบบนี้ถูกพัฒนาด้วย React framework , material ui , nodejs และ MySql โดยระบบสามารถให้ลูกค้าทำการจองคิว ดูคิวว่างของร้านเสริมสวย หาตำแหน่งของร้าน ดูข้อมูลทั่วไปของร้าน และเขียนรีวิวติชมได้ ในส่วนของเจ้าของร้าน สามารถเพิ่มข้อมูลทั่วไปของร้าน และจัดการการจองคิวของร้านเสริมสวยได้ ระบบการจองคิวรองรับการแสดงผลบนอุปกรณ์สมาร์ทโพนและเว็บบราวเซอร์ ระบบที่พัฒนาขึ้นจะช่วยอำนวยความสะดวกในการนัดหมายล่วงหน้า ลดการรอคิวของผู้ใช้บริการ และช่วยให้การจองคิวมีระเบียบมากขึ้น
\noindent
\\คำสำคัญ: เว็บแอปพลิเคชัน , ระบบจองคิวร้านเสริมสวย
\end{thaiabstract}
\begin{acknowledgements} %TODO update here!
การพัฒนาโครงงานระบบการจองคิวร้านเสริมสวย สำเร็จลุล่วงได้ด้วยความกรุณาและความช่วยเหลือจากหลายๆ ฝ่าย ถึงแม้จะประสบกับปัญาหาในการทำโครงงานก็ได้รับคำแนะนำและการช่วยเหลือที่ดีเสมอมา ข้าพเจ้าจึงใคร่ขอขอบคุณทุกๆท่าน ที่มีส่วนร่วมในการพัฒนาโครงานนี้
ขอขอบพระคุณอาจารย์ ดร.ทศพร จูฉิม อาจารย์ที่ปรึกษาโครงงานที่ได้แนะนำทฤษฎีและแนวทางในแก้ปัญหาต่าง ๆ ที่เกิดขึ้นระหว่างการพัฒนาระบบและคอยตรวจสอบความก้าวหน้าของโครงงาน รวมทั้งยังคอยให้กำลังใจในการพัฒนาโครงงานนี้ตลอดจนโครงงานเสร็จสมบูรณ์
ขอบพระคุณอาจารย์ประจำสาขาวิทยาการคอมพิวเตอร์ อาจารย์ประจำภาควิชาคณิตศาสตร์ สถิติ และคอมพิวเตอร์ และอาจารย์ในคณะวิทยาศาสตร์ทุก ๆ ท่าน ที่คอยให้คำแนะนำ อบรมสั่งสอน และคอยช่วยเหลือข้าพเจ้าในการศึกษาตลอดมาขอบคุณเจ้าหน้าที่และบุคลากรของคณะวิทยาศาสตร์ ที่ได้อำนวยความสะดวกทางด้านอุปกรณ์และเครื่องมือต่าง ๆ
ขอบพระคุณบิดา มารดา ที่เป็นกำลังใจ คอยให้ความรักและความห่วงใยเสมอมา ตลอดจนคอยช่วยเหลือทุนทรัพย์ทางด้านการศึกษา และอุปกรณ์ในการพัฒนาโครงงาน
ขอบคุณเพื่อน ๆ สาขาวิทยาการคอมพิวเตอร์ชั้นปีที่ 4 ที่ได้คอยช่วยแก้ไขปัญหาและให้คำปรึกษาในการพัฒนาโครงงานครั้งนี้จนเสร็จสิ้น
\end{acknowledgements}
\begin{flushright}
นางสาวปิยพร อาภรศรี \\
%\vspace{-5mm}
วันที่ 28 กุมภาพันธ์ 63
\end{flushright}
\chapter{การติดตั้งเครื่องมือที่ใช้พัฒนาโปรแกรม}
การติดตั้งเครื่องมือที่ใช้ในการพัฒนาเว็บแอพพลิเคชั่นระบบแนะนำสถานที่ท่องเที่ยวในจังหวัดอุบลราชธานีมีโปรแกรมที่จำเป็นในการพัฒนาระบบดังต่อไปนี้
\begin{itemize}
\item การติดตั้ง Visual Studio Code
\item การติดตั้ง Node.js
\item การติดตั้ง Angular Fronted Framwork
\end{itemize}
\section{การติดตั้ง Visual Studio Code}
\begin{enumerate}
\item สามารถดาวน์โหลด Visual Studio Code ได้ที่ https://code.visualstudio.com/download ดังแสดงในรูปที่ \ref{Fig:vscode}
\begin{figure}[H]
\includegraphics[width=\columnwidth]{Figures/prepareation/vscode}
\caption{หน้าเว็บดาวน์โหลด Visual Studio Code}
\label{Fig:vscode}
\end{figure}
\newpage
\item เมื่อเปิดตัวติดตั้งขึ้นมาแล้ว จะแสดงหน้าจอ Welcome to the Visual Studio Code Setup Wizard ให้กดปุ่ม Next เพื่อเริ่มกระบวนการติดตั้ง ดังแสดงในรูปที่ \ref{Fig:vsi1}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/vsi1}
\caption{หน้าต่างต้อนรับของ Visual Studio Code}
\label{Fig:vsi1}
\end{figure}
\item หลังจากนั้นจะแสดงหน้าต่างข้อตกลงการใช้งาน Visual Studio Code ทำการติ๊กที่ I accept the areement แล้วกด Next ดังแสดงในรูปที่ \ref{Fig:vsi2}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/vsi2}
\caption{หน้าต่างข้อตกลงการใช้งาน Visual Studio Code}
\label{Fig:vsi2}
\end{figure}
\newpage
\item จากนั้นจะแสดงหน้าต่างที่จัดเก็บไฟล์ต่างๆ ของ Visual Studio Code ทำการกด Next ดังแสดงในรูปที่ \ref{Fig:vsi3}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/vsi3}
\caption{หน้าต่างที่จัดเก็บไฟล์ต่างๆ ของ Visual Studio Code}
\label{Fig:vsi3}
\end{figure}
\item จากนั้นจะแสดงหน้าต่างการจัดการซอร์ดคัทของ Visual Studio Code ทำการกด Next ดังแสดงในรูปที่ \ref{Fig:vsi4}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/vsi4}
\caption{หน้าต่างการจัดการซอร์ดคัท ของ Visual Studio Code}
\label{Fig:vsi4}
\end{figure}
\newpage
\item จากนั้นแสดงหน้าต่างเริ่มทำการติดตั้งทำการกด Next ดังแสดงในรูปที่ \ref{Fig:vsi5}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/vsi5}
\caption{หน้าต่างเริ่มทำการติดตั้งทำการกด ของ Visual Studio Code}
\label{Fig:vsi5}
\end{figure}
\item จากนั้นจะแสดงหน้าต่างเมื่อเข้าโปรแกรมหลังติตั้งเสร็จ ดังแสดงในรูปที่ \ref{Fig:vsi6}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/vsi6}
\caption{ หน้าต่างเมื่อเข้าโปรแกรมหลังติตั้งเสร็จ ของ Visual Studio Code}
\label{Fig:vsi6}
\end{figure}
\end{enumerate}
\newpage
\section{การติดตั้ง Node.js}
\begin{enumerate}
\item สามารถดาวน์โหลด Node.js installer package ได้ที่ https://nodejs.org/en/download/ ดังแสดงในรูปที่ \ref{Fig:nodejs}
\begin{figure}[H]
\includegraphics[width=\columnwidth]{Figures/prepareation/nodejs}
\caption{หน้าเว็บดาวน์โหลด Node.js}
\label{Fig:nodejs}
\end{figure}
\item เปิดไฟล์ติดตั้ง ชื่อ node-vx.xx.x-x64.msi เพื่อทำการติดตั้ง ดังแสดงในรูปที่ \ref{Fig:nodei1}
\begin{figure}[H]
\includegraphics[width=\columnwidth]{Figures/prepareation/nodei1}
\caption{ไฟล์ติดตั้งสำหรับติดตั้ง Node.js}
\label{Fig:nodei1}
\end{figure}
\newpage
\item แสดงหน้าต่างต้อนรับของ Node.js ทำการกด Next เพื่อเริ่มกระบวนการติดตั้ง ดังแสดงในรูปที่ \ref{Fig:nodei2}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/nodei2}
\caption{หน้าต่างตอนรับของ Node.js}
\label{Fig:nodei2}
\end{figure}
\item แสดงหน้าต่างข้อตกลงในการใช้ Node.js ให้เลือกช่อง I accept the terms in the License Agreement และกด Next ดังแสดงในรูปที่ \ref{Fig:nodei3}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/nodei3}
\caption{หน้าต่างข้อตกลงในการใช้ Node.js}
\label{Fig:nodei3}
\end{figure}
\newpage
\item แสดงหน้าต่างเลือกโฟลเดอร์ที่จะทำการติดตั้ง ดังแสดงในรูปที่ \ref{Fig:nodei4}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/nodei4}
\caption{หน้าต่างเลือกโฟลเดอร์ที่จะทำการติดตั้ง Node.js}
\label{Fig:nodei4}
\end{figure}
\item แสดงหน้าต่างสำหรับติดตั้ง Node.js ทำการกด Install เพื่อทำการติดตั้ง ดังแสดงในรูปที่ \ref{Fig:nodei5}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/nodei5}
\caption{หน้าต่างติดตั้ง Node.js}
\label{Fig:nodei5}
\end{figure}
\end{enumerate}
\newpage
\section{การติดตั้ง Angular}
\begin{enumerate}
\item สามารถติดตั้ง Angular ได้ที่ https://angular.io/guide/quickstart ดังแสดงในรูปที่ \ref{Fig:angular}
\begin{figure}[H]
\centering
\includegraphics[width=\columnwidth]{Figures/prepareation/angular}
\caption{หน้าเว็บ Angular}
\label{Fig:angular}
\end{figure}
\item เปิด Node.js command prompt ขึ้นมาแล้วใช้คำสั่ง npm install -g @angular/cli ดังแสดงในรูปที่ \ref{Fig:angular1}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/angular1}
\caption{ใช้คำสั่งติดตั้ง Angular}
\label{Fig:angular1}
\end{figure}
\item Node.js จะทำการดาวน์โหลดและติดตั้ง Angular 6 ดังแสดงในรูปที่ \ref{Fig:angular2}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/angular2}
\caption{ใช้คำสั่งติดตั้ง Angular}
\label{Fig:angular2}
\end{figure}
\newpage
\item เข้าสู่ไดเร็กทอรีของ project แล้วพิมพ์คำสั่ง ng new my-project ดังแสดงในรูปที่ \ref{Fig:angular3}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/angular3}
\caption{ใช้คำสั่งสร้าง project}
\label{Fig:angular3}
\end{figure}
\item ng คือ prefix ของคำสั่ง Angular ในที่นี้เราสร้างโปรเจ็กต์ใหม่ชื่อ my-project รอจนดาวน์โหลดเทมเพลตของ project เสร็จเรียบร้อย ดังแสดงในรูปที่\ref{Fig:angular4}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/angular4}
\caption{ใช้คำสั่งสร้าง project}
\label{Fig:angular4}
\end{figure}
\item เข้าสู่ไดเร็กทอรีของ project แล้วพิมพ์คำสั่ง cd my-project เเละ ng serve --open ดังแสดงในรูปที่\ref{Fig:angular5}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/angular5}
\caption{ใช้คำสั่งสร้าง project}
\label{Fig:angular5}
\end{figure}
\newpage
\item คำสั่ง serve จะไปเรียกใช้งาน webpack เพื่อเปิด localhost ที่พอร์ต 4200 ดังแสดงในรูปที่\ref{Fig:angular6}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/angular6}
\caption{การใช้คำสั่งรัน project}
\label{Fig:angular6}
\end{figure}
\item หน้าแรกของเว็บไซต์เเสดงดังรูปที่ \ref{Fig:angular7}
\begin{figure}[H]
\centering
\includegraphics[width=0.7\columnwidth]{Figures/prepareation/angular7}
\caption{หน้าแรกของเว็บไซต์}
\label{Fig:angular7}
\end{figure}
\end{enumerate}
\chapter{คู่มือการติดตั้งระบบ}
ในการติดตั้งเพื่อใช้งานเว็บแอปพลิเคชันระบบจองคิวร้านเสริมสวย สามารถทำได้โดยมีขั้นตอนดังนี้
\begin{enumerate}
\item สามารถ Clone Project ได้ที่ http://projectcs.sci.ubu.ac.th/senior-prj-62/59110440259.git ดังแสดงในรูปที่ \ref{Fig:webubon}
\begin{figure}[H]
\centering
\includegraphics[width=\columnwidth]{Figures/7/installApp/webubon}
\caption{หน้าเว็บ Reposity ของโปรเจ็ค}
\label{Fig:webubon}
\end{figure}
\item เข้าไปในโฟลเดอร์ Source/Web เพื่อติดตั้ง Package โดยเปิด command prompt แล้วใช้คำสั่งดังรูปที่ \ref{Fig:installPK}
\begin{figure}[H]
\centering
{\setstretch{1.0}\begin{lstlisting}
npm install
\end{lstlisting}}
\caption{คำสั่งติดตั้ง Package}
\label{Fig:installPK}
\end{figure}
\item ทำการพิมพ์คำสั่ง npm run start เพื่อเปิด localhost ที่พอร์ต 3000 ดังแสดงในรูปที่ \ref{Fig:dl3}
\begin{figure}[H]
\centering
{\setstretch{1.0}\begin{lstlisting}
npm run start
\end{lstlisting}}
\caption{คำสั่งเริ่มใช้งานโปรเจ็ค}
\label{Fig:dl3}
\end{figure}
\chapter{คู่มือการใช้งานระบบ}
คู่มือการใช้งานทั้งหมดของระบบ สามารถแบ่งออกเป็น 2 ส่วน ดังนี้
\begin{itemize}
\item ส่วนผู้ใช้งาน
\item ส่วนผู้ดูแลระบบ
\end{itemize}
\section{ส่วนผู้ใช้งาน}
\begin{enumerate}
\item ส่วนของหน้าสมัครสมาชิก
\begin{itemize}
\item เมื่อผู้ใช้กดปุ่มสมัครสมาชิกระบบจะแสดงหน้าให้กรอกข้อมูลการสมัครสมาชิก ดังแสดงในรูปที่ \ref{Fig:uiweb1}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/register}
\caption{หน้าจอการสมัครสมาชิก}
\label{Fig:uiweb1}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb1} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ปุ่มเข้าสู่ระบบเพื่อใช้ในการเข้าใช้งานระบบ
\item หมายเลข 2 คือ ช่องกรอกชื่อผู้ใช้
\item หมายเลข 3 คือ ช่องกรอกชื่อ-นามสกุล
\item หมายเลข 4 คือ ช่องกรอกรหัสผ่าน
\item หมายเลข 5 คือ ช่องกรอกรหัสผ่านยืนยัน
\item หมายเลข 6 คือ เลือกเพศ
\item หมายเลข 7 คือ ปุ่มเลือกรูปภาพโปรไฟล์
\item หมายเลข 8 คือ ปุ่มสมัครสมาชิกเพื่อยืนยันการสมัครสมาชิก
\item หมายเลข 9 คือ ปุ่มยกเลิกเมือไม่ต้องการสมัครสมาชิก
\end{itemize}
\item ส่วนของหน้าเข้าสู่ระบบ
\begin{itemize}
\item เมื่อผู้ใช้สมัครสมาชิกสำเร็จผู้ใช้สามารถเข้าสู่ระบบได้ด้วยการกดปุ่มเข้าสู่ระบบ ดังแสดงในรูปที่ \ref{Fig:uiweb2}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/login}
\caption{หน้าจอการเข้าสู่ระบบ}
\label{Fig:uiweb2}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb2} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ช่องกรอกชื่อผู้ใช้
\item หมายเลข 2 คือ ช่องกรอกรหัสผ่าน
\item หมายเลข 3 คือ ปุ่มกดเข้าสู่ระบบ
\item หมายเลข 4 คือ เมือผู้ใช้คลิกสมัครสมาชิกระบบจะแสดงหน้าสมัครสมาชิก
\end{itemize}
\newpage
\item ส่วนของหน้าจัดอันดับสถานที่ยอดนิยม
\begin{itemize}
\item เมื่อผู้ใช้กดเลือกเมนูสถานที่ยอดนิยมระบบจะแสดงหน้าจัดอันดับสถานที่ยอดนิยมให้แก่ผู้ใช้ได้ดู ดังแสดงในรูปที่ \ref{Fig:uiweb3}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/popular}
\caption{หน้าจอการจัดอันดับสถานที่ยอดนิยม}
\label{Fig:uiweb3}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb3} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ เมนูสถานที่ยอดนิยม จะแสดงข้อมูลสถานที่ยอดนิมโดยจะให้ผู้ใช้สามารถเลือกกลุ่มที่จะดูสถานทียอดนิยมได้
\item หมายเลข 2 คือ เมนูที่เที่ยว จะแสดงหน้าให้ผู้ใช้เลือกหมวดทที่เที่ยว
\item หมายเลข 3 คือ เมนูที่กิน จะแสดงหน้าให้ผู้ใช้เลือกหมวดที่กิน
\item หมายเลข 4 คือ เมนูที่พัก จะแสดงหน้าให้ผู้ใช้เลือกหมวดที่พัก
\item หมายเลข 5 คือ เมนูแนะนำสถานที่ จะแสดงหน้าข้อมูลให้ผู้ใช้กรอกแบบประเมิน
\item หมายเลข 6 คือ ปุ่มโปรไฟล์ โดยผู้ใช้สามารถดูข้อมูลโปรไฟล์ของตัวเองได้
\item หมายเลข 7 คือ เป็นส่วนของการให้ผู้ใช้เลือกปประเภทที่ต้องการกดูข้อมูลสถานที่ยอดนิยม
\item หมายเลข 8 คือ แสดงข้อมูลรายละเอียกสถานที่ยอดนิยมโดยเรียงจากอันดับที่หนึ่งไปหาอันกับสุดท้าย
\end{itemize}
\newpage
\item ส่วนของหน้าหมวดสถานที่กิน
\begin{itemize}
\item เมื่อผู้ใช้กดเลือกเมนูสถานที่กินระบบจะแสดงหน้าประเภทของร้านอาหารให้แก่ผู้ใช้ได้ดู ดังแสดงในรูปที่ \ref{Fig:uiweb4}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/eat}
\caption{หน้าจอหมวดที่กิน}
\label{Fig:uiweb4}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb4} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ปุ่มเลือกประเภทข้อมูลที่ต้องการที่จะค้นหา
\item หมายเลข 2 คือ ช่องกรอกชื่อสถานที่ที่ต้องการค้นหา
\item หมายเลข 3 คือ ปุ่มค้นหา เมื่อกรอกชื่อสถานที่เสร็จสามารถกดปุ่มค้นหาได้
\item หมายเลข 4 คือ แสดงข้อมูลหมวดต่างๆเพื่อให้ผู้ใช้ได้เลือกดูข้อมูลตามหมวด
\end{itemize}
\newpage
\item ส่วนของข้อมูลสถานที่
\begin{itemize}
\item เมื่อผู้ใช้กดเลือกสถานที่เที่ยวระบบจะแสดงหน้ารายละเอียดสถานที่ให้แก่ผู้ใช้ได้ดู ดังแสดงในรูปที่ \ref{Fig:uiweb5}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/listplace}
\caption{หน้าจอแสดงข้อมูลสถานที่}
\label{Fig:uiweb5}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb5} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ส่วนของการแสดงข้อมูลสถานที่โดยจะประกอบด้วย ชื่อสถานที่ ที่อยู่ คะแนนเฉลี่ยของสถานที่ และรูปภาพสถานที่
\item หมายเลข 2 คือ ปุ่มเพิ่มข้อมูล เมื่อผู้ใช้ต้องการที่จะเพิ่มข้อมูลสถานที่ใหม่เข้าไปในระบบเพื่อให้คนอื่นได้รู้จัก
\end{itemize}
\item ส่วนของหน้ารายละเอียดสถานที่
\begin{itemize}
\item เมื่อผู้ใช้กดเลือกสถานที่เที่ยวระบบจะแสดงหน้ารายละเอียดสถานที่ให้แก่ผู้ใช้ได้ดู ดังแสดงในรูปที่ \ref{Fig:uiweb6}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/detail}
\caption{หน้าจอแดงรายละเอียดสถานที่}
\label{Fig:uiweb6}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb6} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ปุ่มเขียนรีริว ใช้ในการแสดงหน้าเขียนรีวิวเมื่อผู้ใช้ต้องการที่จะเขียนรีวิว
\item หมายเลข 2 คือ ปุ่มบันทึก ใช้ในการบันทึกสถานที่ไว้เพื่อเข้าไปอ่านข้อมูลรายเอียดที่หลัง
\item หมายเลข 3 คือ ปุ่มแจ้งแก้ไข ใช้ในการแสดงหน้าแจ้งแก้ไขเมื่อผู้ใช้เห็นว่าข้อมูลในส่วนนี้ไม่ถูกต้อง
\item หมายเลข 4 คือ แสดงรายละเอียดข้อมูลของสถานที่
\item หมายเลข 5 คือ ส่วนที่ใช้ในการแสดงแผนที่
\end{itemize}
\newpage
\item ส่วนของหน้าแสดงโปรไฟล์ผู้ใช้
\begin{itemize}
\item เมื่อผู้ใช้กดเลือกโปรไฟล์ระบบจะแสดงหน้าข้อมูลรายละเอียดของผู้ใช้ ดังแสดงในรูปที่ \ref{Fig:uiweb7}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/profile}
\caption{หน้าจอแสดงข้อมูลโปรไฟล์ของผู้ใช้}
\label{Fig:uiweb7}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb7} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ปุ่มแก้ไขโปรไฟล์ โดยจะแสดงหน้าแก้ไขซึ่งจะสามารถแก้ไขรูปภาพ ชื่อผู้ใช้ เปลี่ยนรหัสผ่านได้
\item หมายเลข 2 คือ เมนูที่บันทึกไว้ ถ้าผู้ใช้กดเมนูนี้จะแสดงข้อมูลสถานที่ที่ผู้ใช้ได้ทำการกดบันทึกไว้
\item หมายเลข 3 คือ เมนูรีวิว ถ้าผู้ใช้กดเมนูนี้จะแสดงข้อมูลสถานที่ที่ผู้ใช้ได้ทำการเขียนรีวิวไว้
\item หมายเลข 4 คือ เมนูสถานที่เพิ่ม ถ้าผู้ใช้กดเเมนูนี้จะแสดงข้อมูลสถานที่ที่ผู้ใช้ได้เพิ่มลงในระบบ
\item หมายเลข 5 คือ ส่วนที่ใช้แสดงข้อมูลสถานที่ต่างๆตามเมนูที่ผู้ใช้เลือก
\end{itemize}
\newpage
\item ส่วนของหน้าเขียนรีวิวและให้คะแนน
\begin{itemize}
\item เมื่อผู้ใช้กดปุ่มเขียนรีวิวระบบจะแสดงหน้าเขียนรีวิว ดังแสดงในรูปที่ \ref{Fig:uiweb8}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/review}
\caption{หน้าจอแสดงเขียนรีวิว}
\label{Fig:uiweb8}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb8} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ส่วนของการให้คะแนนสถานที่
\item หมายเลข 2 คือ ช่องกรอกหัวข้อรีวิว
\item หมายเลข 3 คือ ช่องกรอกรายละเอียดรีวิว
\item หมายเลข 4 คือ ปุ่มเพิ่มรูปภาพของรีวิว
\item หมายเลข 5 คือ ปุ่มบันทึกรีวิว
\item หมายเลข 6 คือ ปุ่มยกเลิกการเขียนรีวิว
\end{itemize}
\end{enumerate}
\newpage
\section{ส่วนผู้ดูแลระบบ}
\begin{enumerate}
\item ส่วนของการจัดการข้อมูล
\begin{itemize}
\item เมื่อผู้ดูแลระบบเข้าสู่ระบบสำเร็จ จะเข้ามาหน้าจัดการข้อมูลสถานที่ ดังแสดงในรูปที่ \ref{Fig:uiweb9}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/admin1}
\caption{หน้าจอจัดการข้อมูลสถานที่เที่ยว}
\label{Fig:uiweb9}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb9} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ เมนูแจ้งแก้ไข จะแสดงข้อมูลการแจ้งแก้ไขมาจากฝั่งของผู้ใช้
\item หมายเลข 2 คือ เมนูข้อมูลที่เที่ยว จะแสดงข้อมูลสถานที่เที่ยวทั้งหมดเพื่อให้ผู้ดูแลระบบสามารถจัดการข้อมูลได้
\item หมายเลข 3 คือ เมนูข้อมูลที่กิน จะแสดงข้อมูลสถานที่กินทั้งหมดเพื่อให้ผู้ดูแลระบบสามารถจัดการข้อมูลได้
\item หมายเลข 4 คือ เมนูข้อมูลที่พัก จะแสดงข้อมูลสถานที่พักทั้งหมดเพื่อให้ผู้ดูแลระบบสามารถจัดการข้อมูลได้
\item หมายเลข 5 คือ เมนู train ผู้ดูแลระบบสามารถ train model ใหม่ทุกครั้งได้
\item หมายเลข 6 คือ ปุ่มออกจากระบบ
\item หมายเลข 7 คือ ปุ่มเลือกประเภทเพื่อดูข้อมูลแค่ประเภทนั้นๆ
\item หมายเลข 8 คือ แสดงรายละเอียดข้อมูล
\item หมายเลข 9 คือ ปุ่มเพิ่มข้อมูลสถานที่
\item หมายเลข 10 คือ ปุ่มแก้ไขข้อมูลสถานที่
\item หมายเลข 11 คือ ปุ่มลบข้อมูลสถานที่
\end{itemize}
\newpage
\item ส่วนของการจัดการข้อมูลเเพิ่มข้อมูลถานที่
\begin{itemize}
\item เมื่อผู้ดูแลระบบเข้าสู่ระบบและต้องการเพิ่มข้อมูลสามารถกดปุ่มเพิ่มข้อมูล ระบบจะแสดงหน้าเพิ่มให้กรอกข้อมูลสถานที่ ดังแสดงในรูปที่ \ref{Fig:uiweb10}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/add}
\caption{หน้าจอการเพิ่มสถานที่}
\label{Fig:uiweb10}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb10} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ส่วนที่แสดงให้ผู้ดูแลระบบกรอกข้อมูลสถานที่ที่ต้องการเพิ่มข้อมูล
\item หมายเลข 2 คือ ปุ่มบันทึก เพื่อเป็นการยืนยันการเพิ่มสถานที่
\item หมายเลข 3 คือ ปุ่มยกเลิก เพื่อเป็นการยืนยันยกเลิกการเพิ่มสถานที่
\end{itemize}
\newpage
\item ส่วนของการจัดการข้อมูลแก้ไขสถานที่
\begin{itemize}
\item เมื่อผู้ดูแลระบบเข้าสู่ระบบและต้องการแก้ไขข้อมูลสามารถกดปุ่มแก้ไข และระบบจะแสดงหน้าแก้ไข ดังแสดงในรูปที่ \ref{Fig:uiweb11}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/edit}
\caption{หน้าจอการแก้ไขข้อมูลสถานที่}
\label{Fig:uiweb11}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb11} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ส่วนแสดงข้อมูลรายละเอียดของสถานที่
\item หมายเลข 2 คือ ปุ่มบันทึกการแก้ไข เมื่อผู้ดูแลระบบต้องการที่จะบันทึกข้อมูลที่แก้ไข
\item หมายเลข 3 คือ ปุ่มยกเลิกการแก้ไข
\end{itemize}
\newpage
\item ส่วนของหน้าจัดการข้อมูลแจ้งแก้ไข
\begin{itemize}
\item เมื่อผู้ดูแลระบบเข้าสู่ระบบและต้องการจัดการข้อมูลแจ้งแก้ไขสามารถกดที่เมนูแจ้งแก้ไข ระบบจะแสดงหน้าแจ้งแก้ไข ดังแสดงในรูปที่ \ref{Fig:uiweb12}\textbf{}
\begin{figure}[H]
\centering
\includegraphics[width=420]{Figures/7/web/admin2}
\caption{หน้าจอการแจ้งแก้ไข}
\label{Fig:uiweb12}
\end{figure}
\end{itemize}
จากรูปที่ \ref{Fig:uiweb12} สามารถอธิบายการใช้งานได้ดังนี้
\begin{itemize}[label={--}]
\item หมายเลข 1 คือ ปุ่มแก้ไข เมื่อผู้ดูแลระบบต้องการแก้ไขข้อมูลตัวที่ผู้ใช้แจ้งเข้ามา
\item หมายเลข 2 คือ รายละเอียดข้อมูลผู้แจ้งแก้ไขและชื่อสถานที่ที่แจ้งแกไข
\end{itemize}
\end{enumerate}
\ No newline at end of file
{\setstretch{1.0}\begin{biography}
\justify
ชื่อ-สกุล: นางสาว ปิยพร อาภรศรี \\
รหัสประจำตัวนักศึกษา: 59110440259\\
วันเกิด: 12 06 2540\\
ที่อยู่ที่สามารถติดต่อได้: 7 หมู่ 1 ต.หนองบก อ.เหล่าเสือโก้ก จ.อุบลราชธานี 34000\\
เบอร์โทรศัพท์: (+66) 64 718 3784\\
อีเมล: piyaphorn.ar.59@ubu.ac.th\\
ระดับมัธยมต้น: โรงเรียนหกสิบพรรษาวิทยาคม อุบลราชธานี\\
ระดับมัธยมปลาย: โรงเรียนหกสิบพรรษาวิทยาคม อุบลราชธานี\\
ระดับอุดมศึกษา: ภาควิชาคณิตศาสตร์ สถิติ และคอมพิวเตอร์ สาขาวิทยาการคอมพิวเตอร์ คณะวิทยาศาสตร์ มหาวิทยาลัยอุบลราชธานี
\end{biography}}
%% File: `chulanat.bst'
%% A modification of `plainnat.bst' for use with natbib package
%%
%% Author: Supasate Choochaisri
%% - modify to handle various Chula reference formats.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Changelog
%% July 6, 2015
%% - add "" to format.article.pages to prevent empty pop
%% Copyright of `plainnat.bst' 1993-2007 Patrick W Daly
%% Max-Planck-Institut f\"ur Sonnensystemforschung
%% Max-Planck-Str. 2
%% D-37191 Katlenburg-Lindau
%% Germany
%% E-mail: daly@mps.mpg.de
%%
%% This program can be redistributed and/or modified under the terms
%% of the LaTeX Project Public License Distributed from CTAN
%% archives in directory macros/latex/base/lppl.txt; either
%% version 1 of the License, or any later version.
%%
% Version and source file information:
% \ProvidesFile{natbst.mbs}[2007/02/19 1.92 (PWD)]
%
% BibTeX `plainnat' family
% version 0.99b for BibTeX versions 0.99a or later,
% for LaTeX versions 2.09 and 2e.
%
% For use with the `natbib.sty' package; emulates the corresponding
% member of the `plain' family, but with author-year citations.
%
% With version 6.0 of `natbib.sty', it may also be used for numerical
% citations, while retaining the commands \citeauthor, \citefullauthor,
% and \citeyear to print the corresponding information.
%
% For version 7.0 of `natbib.sty', the KEY field replaces missing
% authors/editors, and the date is left blank in \bibitem.
%
% Includes field EID for the sequence/citation number of electronic journals
% which is used instead of page numbers.
%
% Includes fields ISBN and ISSN.
%
% Includes field URL for Internet addresses.
%
% Includes field DOI for Digital Object Idenfifiers.
%
% Works best with the url.sty package of Donald Arseneau.
%
% Works with identical authors and year are further sorted by
% citation key, to preserve any natural sequence.
%
ENTRY
{ address
author
booktitle
chapter
doi
eid
edition
editor
howpublished
institution
isbn
issn
journal
key
month
note
number
organization
pages
publisher
school
series
title
type
url
volume
year
day
}
{}
{ label extra.label sort.label short.list }
INTEGERS { output.state before.all mid.sentence after.sentence after.block }
FUNCTION {init.state.consts}
{ #0 'before.all :=
#1 'mid.sentence :=
#2 'after.sentence :=
#3 'after.block :=
}
STRINGS { s t }
FUNCTION {output.nonnull}
{ 's :=
output.state mid.sentence =
{ ", " * write$ }
{ output.state after.block =
{ add.period$ write$
newline$
"\newblock " write$
}
{ output.state before.all =
'write$
{ add.period$ " " * write$ }
if$
}
if$
mid.sentence 'output.state :=
}
if$
s
}
FUNCTION {output}
{ duplicate$ empty$
'pop$
'output.nonnull
if$
}
FUNCTION {output.check}
{ 't :=
duplicate$ empty$
{ pop$ "empty " t * " in " * cite$ * warning$ }
'output.nonnull
if$
}
FUNCTION {fin.entry}
{ add.period$
write$
newline$
}
FUNCTION {new.block}
{ output.state before.all =
'skip$
{ after.block 'output.state := }
if$
}
FUNCTION {new.sentence}
{ output.state after.block =
'skip$
{ output.state before.all =
'skip$
{ after.sentence 'output.state := }
if$
}
if$
}
FUNCTION {not}
{ { #0 }
{ #1 }
if$
}
FUNCTION {and}
{ 'skip$
{ pop$ #0 }
if$
}
FUNCTION {or}
{ { pop$ #1 }
'skip$
if$
}
FUNCTION {new.block.checka}
{ empty$
'skip$
'new.block
if$
}
FUNCTION {new.block.checkb}
{ empty$
swap$ empty$
and
'skip$
'new.block
if$
}
FUNCTION {new.sentence.checka}
{ empty$
'skip$
'new.sentence
if$
}
FUNCTION {new.sentence.checkb}
{ empty$
swap$ empty$
and
'skip$
'new.sentence
if$
}
FUNCTION {field.or.null}
{ duplicate$ empty$
{ pop$ "" }
'skip$
if$
}
FUNCTION {emphasize}
{ duplicate$ empty$
{ pop$ "" }
{ "\emph{" swap$ * "}" * }
if$
}
INTEGERS { nameptr namesleft numnames }
FUNCTION {format.names}
{ 's :=
#1 'nameptr :=
s num.names$ 'numnames :=
numnames 'namesleft :=
{ namesleft #0 > }
{ s nameptr "{vv~}{ll}{, f.}{, jj.}" format.name$ 't :=
nameptr #1 >
{ namesleft #1 >
{ ", " * t * }
{ numnames #2 >
{ "," * }
'skip$
if$
t "others" =
{ " et~al." * }
{ " and " * t * }
if$
}
if$
}
't
if$
nameptr #1 + 'nameptr :=
namesleft #1 - 'namesleft :=
}
while$
}
FUNCTION {format.key}
{ empty$
{ key field.or.null }
{ "" }
if$
}
FUNCTION {format.authors}
{ author empty$
{ "" }
{ author format.names }
if$
}
FUNCTION {format.editors}
{ editor empty$
{ "" }
{ editor format.names
editor num.names$ #1 >
{ " (ed.)" * }
{ " (ed.)" * }
if$
}
if$
}
FUNCTION {format.isbn}
{ isbn empty$
{ "" }
{ new.block "ISBN " isbn * }
if$
}
FUNCTION {format.issn}
{ issn empty$
{ "" }
{ new.block "ISSN " issn * }
if$
}
FUNCTION {format.url}
{ url empty$
{ "" }
{ new.block "จาก " url * " " * }
if$
}
FUNCTION {format.doi}
{ doi empty$
{ "" }
{ new.block "\doi{" doi * "}" * }
if$
}
FUNCTION {format.title}
{ title empty$
{ "" }
{ title "t" change.case$ }
if$
}
FUNCTION {format.full.names}
{'s :=
#1 'nameptr :=
s num.names$ 'numnames :=
numnames 'namesleft :=
{ namesleft #0 > }
{ s nameptr
"{vv~}{ll}" format.name$ 't :=
nameptr #1 >
{
namesleft #1 >
{ ", " * t * }
{
numnames #2 >
{ "," * }
'skip$
if$
t "others" =
{ " et~al." * }
{ " and " * t * }
if$
}
if$
}
't
if$
nameptr #1 + 'nameptr :=
namesleft #1 - 'namesleft :=
}
while$
}
FUNCTION {author.editor.full}
{ author empty$
{ editor empty$
{ "" }
{ editor format.full.names }
if$
}
{ author format.full.names }
if$
}
FUNCTION {author.full}
{ author empty$
{ "" }
{ author format.full.names }
if$
}
FUNCTION {editor.full}
{ editor empty$
{ "" }
{ editor format.full.names }
if$
}
FUNCTION {make.full.names}
{ type$ "book" =
type$ "inbook" =
or
'author.editor.full
{ type$ "proceedings" =
'editor.full
'author.full
if$
}
if$
}
FUNCTION {output.bibitem}
{ newline$
"\bibitem[" write$
label write$
")" make.full.names duplicate$ short.list =
{ pop$ }
{ * }
if$
"]{" * write$
cite$ write$
"}" write$
newline$
""
before.all 'output.state :=
}
FUNCTION {n.dashify}
{ 't :=
""
{ t empty$ not }
{ t #1 #1 substring$ "-" =
{ t #1 #2 substring$ "--" = not
{ "--" *
t #2 global.max$ substring$ 't :=
}
{ { t #1 #1 substring$ "-" = }
{ "-" *
t #2 global.max$ substring$ 't :=
}
while$
}
if$
}
{ t #1 #1 substring$ *
t #2 global.max$ substring$ 't :=
}
if$
}
while$
}
FUNCTION {format.date}
{ year duplicate$ empty$
{ "empty year in " cite$ * warning$
pop$ "" }
'skip$
if$
month empty$
'skip$
{ month
" " * swap$ *
}
if$
extra.label *
}
FUNCTION {format.year}
{ year duplicate$ empty$
{ "empty year in " cite$ * warning$
pop$ "" }
'skip$
if$
extra.label *
}
FUNCTION {format.btitle}
{ title emphasize
}
FUNCTION {tie.or.space.connect}
{ duplicate$ text.length$ #3 <
{ "~" }
{ " " }
if$
swap$ * *
}
FUNCTION {either.or.check}
{ empty$
'pop$
{ "can't use both " swap$ * " fields in " * cite$ * warning$ }
if$
}
FUNCTION {format.bvolume}
{ volume empty$
{ "" }
{ "volume" volume tie.or.space.connect
series empty$
'skip$
{ " of " * series emphasize * }
if$
"volume and number" number either.or.check
}
if$
}
FUNCTION {format.number.series}
{ volume empty$
{ number empty$
{ series field.or.null }
{ output.state mid.sentence =
{ "number" }
{ "Number" }
if$
number tie.or.space.connect
series empty$
{ "there's a number but no series in " cite$ * warning$ }
{ " in " * series * }
if$
}
if$
}
{ "" }
if$
}
FUNCTION {format.edition}
{ edition empty$
{ "" }
{ output.state mid.sentence =
{ edition "l" change.case$ " edition" * }
{ edition "t" change.case$ " edition" * }
if$
}
if$
}
INTEGERS { multiresult }
FUNCTION {multi.page.check}
{ 't :=
#0 'multiresult :=
{ multiresult not
t empty$ not
and
}
{ t #1 #1 substring$
duplicate$ "-" =
swap$ duplicate$ "," =
swap$ "+" =
or or
{ #1 'multiresult := }
{ t #2 global.max$ substring$ 't := }
if$
}
while$
multiresult
}
FUNCTION {format.pages}
{ pages empty$
{ "" }
{ pages multi.page.check
{ "pp." pages n.dashify tie.or.space.connect }
{ "p." pages tie.or.space.connect }
if$
}
if$
}
FUNCTION {format.eid}
{ eid empty$
{ "" }
{ "art." eid tie.or.space.connect }
if$
}
FUNCTION {format.vol.num.pages}
{ volume field.or.null
number empty$
'skip$
{ "\penalty0 ," number * "" * *
volume empty$
{ "there's a number but no volume in " cite$ * warning$ }
'skip$
if$
}
if$
pages empty$
'skip$
{ duplicate$ empty$
{ pop$ format.pages }
{ ":\penalty0 " * pages n.dashify * }
if$
}
if$
}
FUNCTION {format.vol.num.eid}
{ volume field.or.null
number empty$
'skip$
{ "\penalty0 ," number * "" * *
volume empty$
{ "there's a number but no volume in " cite$ * warning$ }
'skip$
if$
}
if$
eid empty$
'skip$
{ duplicate$ empty$
{ pop$ format.eid }
{ ":\penalty0 " * eid * }
if$
}
if$
}
FUNCTION {format.chapter.pages}
{ chapter empty$
'format.pages
{ type empty$
{ "chapter" }
{ type "l" change.case$ }
if$
chapter tie.or.space.connect
pages empty$
'skip$
{ ", " * format.pages * }
if$
}
if$
}
FUNCTION {format.in.ed.booktitle}
{ booktitle empty$
{ "" }
{ editor empty$
{ "In " booktitle emphasize * }
{ "In " format.editors * ", " * booktitle emphasize * }
if$
}
if$
}
FUNCTION {empty.misc.check}
{ author empty$ title empty$ howpublished empty$
month empty$ year empty$ note empty$
and and and and and
key empty$ not and
{ "all relevant fields are empty in " cite$ * warning$ }
'skip$
if$
}
FUNCTION {format.thesis.type}
{ type empty$
'skip$
{ pop$
type "t" change.case$
}
if$
}
FUNCTION {format.tr.number}
{ type empty$
{ "Technical Report" }
'type
if$
number empty$
{ "t" change.case$ }
{ number tie.or.space.connect }
if$
}
FUNCTION {format.article.pages}
{ pages empty$
{ "" }
{ pages multi.page.check
{"" pages n.dashify tie.or.space.connect }
{"" pages tie.or.space.connect }
if$
}
if$
}
FUNCTION {format.article.crossref}
{ key empty$
{ journal empty$
{ "need key or journal for " cite$ * " to crossref " * crossref *
warning$
""
}
{ "In \emph{" journal * "}" * }
if$
}
{ "In " }
if$
" \citet{" * crossref * "}" *
}
FUNCTION {format.book.crossref}
{ volume empty$
{ "empty volume in " cite$ * "'s crossref of " * crossref * warning$
"In "
}
{ "Volume" volume tie.or.space.connect
" of " *
}
if$
editor empty$
editor field.or.null author field.or.null =
or
{ key empty$
{ series empty$
{ "need editor, key, or series for " cite$ * " to crossref " *
crossref * warning$
"" *
}
{ "\emph{" * series * "}" * }
if$
}
'skip$
if$
}
'skip$
if$
" \citet{" * crossref * "}" *
}
FUNCTION {format.incoll.inproc.crossref}
{ editor empty$
editor field.or.null author field.or.null =
or
{ key empty$
{ booktitle empty$
{ "need editor, key, or booktitle for " cite$ * " to crossref " *
crossref * warning$
""
}
{ "In \emph{" booktitle * "}" * }
if$
}
{ "In " }
if$
}
{ "In " }
if$
" \citet{" * crossref * "}" *
}
FUNCTION {article}
{ output.bibitem
format.authors "author" output.check
author format.key output
new.block
format.year "year" output.check
new.block
format.title "title" output.check
write$ ". "
new.block
write$ journal emphasize " " *
write$ volume
number empty$
{ }
{ write$ "."
write$ number
}
if$
write$ " ("
month empty$
{ }
{ write$ month
write$ " "
}
if$
write$ year
write$ "):"
write$ ""
format.article.pages output
fin.entry
}
FUNCTION {book}
{ output.bibitem
author empty$
{ format.editors "author and editor" output.check
editor format.key output
}
{ format.authors output.nonnull
crossref missing$
{ "author and editor" editor either.or.check }
'skip$
if$
}
if$
new.block
format.year "year" output.check
new.block
format.btitle "title" output.check
crossref missing$
{ format.bvolume output
new.block
format.number.series output
new.sentence
publisher "publisher" output.check
address output
}
{ new.block
format.book.crossref output.nonnull
}
if$
format.edition output
format.isbn output
format.doi output
format.url output
new.block
note output
fin.entry
}
FUNCTION {booklet}
{ output.bibitem
format.authors output
author format.key output
new.block
format.title "title" output.check
howpublished address new.block.checkb
howpublished output
address output
format.date output
format.isbn output
format.doi output
format.url output
new.block
note output
fin.entry
}
FUNCTION {inbook}
{ output.bibitem
author empty$
{ format.editors "author and editor" output.check
editor format.key output
}
{ format.authors output.nonnull
crossref missing$
{ "author and editor" editor either.or.check }
'skip$
if$
}
if$
new.block
format.year "year" output.check
new.block
format.btitle "title" output.check
crossref missing$
{ format.bvolume output
format.chapter.pages "chapter and pages" output.check
new.block
format.number.series output
new.sentence
publisher "publisher" output.check
address output
}
{ format.chapter.pages "chapter and pages" output.check
new.block
format.book.crossref output.nonnull
}
if$
format.edition output
format.isbn output
format.doi output
format.url output
new.block
note output
fin.entry
}
FUNCTION {incollection}
{ output.bibitem
format.authors "author" output.check
author format.key output
new.block
format.year "year" output.check
new.block
format.title "title" output.check
new.block
crossref missing$
{ format.in.ed.booktitle "booktitle" output.check
format.bvolume output
format.number.series output
format.chapter.pages output
write$ ". "
new.block
write$ address
write$ ": "
write$ publisher
format.edition output
}
{ format.incoll.inproc.crossref output.nonnull
format.chapter.pages output
}
if$
fin.entry
}
FUNCTION {inproceedings}
{ output.bibitem
format.authors "author" output.check
author format.key output
new.block
format.year "year" output.check
new.block
format.title "title" output.check
new.block
crossref missing$
{ format.in.ed.booktitle "booktitle" output.check
format.bvolume output
format.number.series output
format.pages output
}
{ format.incoll.inproc.crossref output.nonnull
format.pages output
}
if$
write$ ". "
new.block
write$ address
write$ ": "
write$ publisher
fin.entry
}
FUNCTION {conference} { inproceedings }
FUNCTION {manual}
{ output.bibitem
format.authors output
author format.key output
new.block
format.year output
new.block
format.btitle "title" output.check
organization address new.block.checkb
organization output
address output
format.edition output
format.url output
fin.entry
}
FUNCTION {mastersthesis}
{ output.bibitem
format.authors "author" output.check
author format.key output
new.block
format.year "year" output.check
new.block
format.title "title" output.check
new.block
"Master's thesis" format.thesis.type output.nonnull
school "school" output.check
address output
format.url output
fin.entry
}
FUNCTION {misc}
{ output.bibitem
format.authors output
author format.key output
title howpublished new.block.checkb
new.block
write$ " "
write$ "("
write$ year
write$ ")"
format.title output
howpublished new.block.checka
howpublished output
new.block
url empty$
{ }
{ write$ " [ออนไลน์]. "
write$ "สืบค้นเมื่อ "
write$ note
}
if$
new.block
format.url output
fin.entry
empty.misc.check
}
FUNCTION {phdthesis}
{ output.bibitem
format.authors "author" output.check
author format.key output
new.block
format.year "year" output.check
new.block
format.btitle "title" output.check
new.block
"PhD thesis" format.thesis.type output.nonnull
school "school" output.check
address output
format.url output
fin.entry
}
FUNCTION {proceedings}
{ output.bibitem
format.editors output
editor format.key output
new.block
format.year "year" output.check
format.btitle "title" output.check
format.bvolume output
format.number.series output
address output
new.sentence
organization output
publisher output
format.isbn output
format.doi output
format.url output
fin.entry
}
FUNCTION {techreport}
{ output.bibitem
format.authors "author" output.check
author format.key output
new.block
format.year "year" output.check
new.block
format.title "title" output.check
new.block
format.tr.number output.nonnull
institution "institution" output.check
address output
fin.entry
}
FUNCTION {unpublished}
{ output.bibitem
format.authors "author" output.check
author format.key output
new.block
format.title "title" output.check
format.date output
format.url output
fin.entry
}
FUNCTION {internet}
{ output.bibitem
title howpublished new.block.checkb
format.title output
howpublished new.block.checka
howpublished output
new.block
url empty$
{ }
{ write$ " [ออนไลน์]. "
write$ "สืบค้นเมื่อ "
write$ day
write$ " "
write$ month
write$ " "
write$ year
}
if$
new.block
format.url output
fin.entry
}
FUNCTION {default.type} { misc }
MACRO {jan} {"January"}
MACRO {feb} {"February"}
MACRO {mar} {"March"}
MACRO {apr} {"April"}
MACRO {may} {"May"}
MACRO {jun} {"June"}
MACRO {jul} {"July"}
MACRO {aug} {"August"}
MACRO {sep} {"September"}
MACRO {oct} {"October"}
MACRO {nov} {"November"}
MACRO {dec} {"December"}
MACRO {acmcs} {"ACM Computing Surveys"}
MACRO {acta} {"Acta Informatica"}
MACRO {cacm} {"Communications of the ACM"}
MACRO {ibmjrd} {"IBM Journal of Research and Development"}
MACRO {ibmsj} {"IBM Systems Journal"}
MACRO {ieeese} {"IEEE Transactions on Software Engineering"}
MACRO {ieeetc} {"IEEE Transactions on Computers"}
MACRO {ieeetcad}
{"IEEE Transactions on Computer-Aided Design of Integrated Circuits"}
MACRO {ipl} {"Information Processing Letters"}
MACRO {jacm} {"Journal of the ACM"}
MACRO {jcss} {"Journal of Computer and System Sciences"}
MACRO {scp} {"Science of Computer Programming"}
MACRO {sicomp} {"SIAM Journal on Computing"}
MACRO {tocs} {"ACM Transactions on Computer Systems"}
MACRO {tods} {"ACM Transactions on Database Systems"}
MACRO {tog} {"ACM Transactions on Graphics"}
MACRO {toms} {"ACM Transactions on Mathematical Software"}
MACRO {toois} {"ACM Transactions on Office Information Systems"}
MACRO {toplas} {"ACM Transactions on Programming Languages and Systems"}
MACRO {tcs} {"Theoretical Computer Science"}
READ
FUNCTION {sortify}
{ purify$
"l" change.case$
}
INTEGERS { len }
FUNCTION {chop.word}
{ 's :=
'len :=
s #1 len substring$ =
{ s len #1 + global.max$ substring$ }
's
if$
}
FUNCTION {format.lab.names}
{ 's :=
s #1 "{vv~}{ll}" format.name$
s num.names$ duplicate$
#2 >
{ pop$ " et~al." * }
{ #2 <
'skip$
{ s #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" =
{ " et~al." * }
{ " and " * s #2 "{vv~}{ll}" format.name$ * }
if$
}
if$
}
if$
}
FUNCTION {author.key.label}
{ author empty$
{ key empty$
{ cite$ #1 #3 substring$ }
'key
if$
}
{ author format.lab.names }
if$
}
FUNCTION {author.editor.key.label}
{ author empty$
{ editor empty$
{ key empty$
{ cite$ #1 #3 substring$ }
'key
if$
}
{ editor format.lab.names }
if$
}
{ author format.lab.names }
if$
}
FUNCTION {author.key.organization.label}
{ author empty$
{ key empty$
{ organization empty$
{ cite$ #1 #3 substring$ }
{ "The " #4 organization chop.word #3 text.prefix$ }
if$
}
'key
if$
}
{ author format.lab.names }
if$
}
FUNCTION {editor.key.organization.label}
{ editor empty$
{ key empty$
{ organization empty$
{ cite$ #1 #3 substring$ }
{ "The " #4 organization chop.word #3 text.prefix$ }
if$
}
'key
if$
}
{ editor format.lab.names }
if$
}
FUNCTION {calc.short.authors}
{ type$ "book" =
type$ "inbook" =
or
'author.editor.key.label
{ type$ "proceedings" =
'editor.key.organization.label
{ type$ "manual" =
'author.key.organization.label
'author.key.label
if$
}
if$
}
if$
'short.list :=
}
FUNCTION {calc.label}
{ calc.short.authors
short.list
"("
*
year duplicate$ empty$
short.list key field.or.null = or
{ pop$ "" }
'skip$
if$
*
'label :=
}
FUNCTION {sort.format.names}
{ 's :=
#1 'nameptr :=
""
s num.names$ 'numnames :=
numnames 'namesleft :=
{ namesleft #0 > }
{
s nameptr "{vv{ } }{ll{ }}{ ff{ }}{ jj{ }}" format.name$ 't :=
nameptr #1 >
{
" " *
namesleft #1 = t "others" = and
{ "zzzzz" * }
{ numnames #2 > nameptr #2 = and
{ "zz" * year field.or.null * " " * }
'skip$
if$
t sortify *
}
if$
}
{ t sortify * }
if$
nameptr #1 + 'nameptr :=
namesleft #1 - 'namesleft :=
}
while$
}
FUNCTION {sort.format.title}
{ 't :=
"A " #2
"An " #3
"The " #4 t chop.word
chop.word
chop.word
sortify
#1 global.max$ substring$
}
FUNCTION {author.sort}
{ author empty$
{ key empty$
{ "to sort, need author or key in " cite$ * warning$
""
}
{ key sortify }
if$
}
{ author sort.format.names }
if$
}
FUNCTION {author.editor.sort}
{ author empty$
{ editor empty$
{ key empty$
{ "to sort, need author, editor, or key in " cite$ * warning$
""
}
{ key sortify }
if$
}
{ editor sort.format.names }
if$
}
{ author sort.format.names }
if$
}
FUNCTION {author.organization.sort}
{ author empty$
{ organization empty$
{ key empty$
{ "to sort, need author, organization, or key in " cite$ * warning$
""
}
{ key sortify }
if$
}
{ "The " #4 organization chop.word sortify }
if$
}
{ author sort.format.names }
if$
}
FUNCTION {editor.organization.sort}
{ editor empty$
{ organization empty$
{ key empty$
{ "to sort, need editor, organization, or key in " cite$ * warning$
""
}
{ key sortify }
if$
}
{ "The " #4 organization chop.word sortify }
if$
}
{ editor sort.format.names }
if$
}
FUNCTION {presort}
{ calc.label
label sortify
" "
*
type$ "book" =
type$ "inbook" =
or
'author.editor.sort
{ type$ "proceedings" =
'editor.organization.sort
{ type$ "manual" =
'author.organization.sort
'author.sort
if$
}
if$
}
if$
" "
*
cite$
*
#1 entry.max$ substring$
'sort.label :=
sort.label *
#1 entry.max$ substring$
'sort.key$ :=
}
ITERATE {presort}
%SORT
STRINGS { longest.label last.label next.extra }
INTEGERS { longest.label.width last.extra.num number.label }
FUNCTION {initialize.longest.label}
{ "" 'longest.label :=
#0 int.to.chr$ 'last.label :=
"" 'next.extra :=
#0 'longest.label.width :=
#0 'last.extra.num :=
#0 'number.label :=
}
FUNCTION {forward.pass}
{ last.label label =
{ last.extra.num #1 + 'last.extra.num :=
last.extra.num int.to.chr$ 'extra.label :=
}
{ "a" chr.to.int$ 'last.extra.num :=
"" 'extra.label :=
label 'last.label :=
}
if$
number.label #1 + 'number.label :=
}
FUNCTION {reverse.pass}
{ next.extra "b" =
{ "a" 'extra.label := }
'skip$
if$
extra.label 'next.extra :=
extra.label
duplicate$ empty$
'skip$
{ "{\natexlab{" swap$ * "}}" * }
if$
'extra.label :=
label extra.label * 'label :=
}
EXECUTE {initialize.longest.label}
ITERATE {forward.pass}
REVERSE {reverse.pass}
FUNCTION {bib.sort.order}
{ sort.label 'sort.key$ :=
}
%ITERATE {bib.sort.order}
%SORT
FUNCTION {begin.bib}
{ preamble$ empty$
'skip$
{ preamble$ write$ newline$ }
if$
"\begin{thebibliography}{" number.label int.to.str$ * "}" *
write$ newline$
"\providecommand{\natexlab}[1]{#1}"
write$ newline$
"\providecommand{\url}[1]{\texttt{#1}}"
write$ newline$
"\expandafter\ifx\csname urlstyle\endcsname\relax"
write$ newline$
" \providecommand{\doi}[1]{doi: #1}\else"
write$ newline$
" \providecommand{\doi}{doi: \begingroup \urlstyle{rm}\Url}\fi"
write$ newline$
}
EXECUTE {begin.bib}
EXECUTE {init.state.consts}
ITERATE {call.type$}
FUNCTION {end.bib}
{ newline$
"\end{thebibliography}" write$ newline$
}
EXECUTE {end.bib}
xelatex -interaction=nonstopmode -file-line-error -synctex=1 -shell-escape CS59110440259.tex
xelatex -interaction=nonstopmode -file-line-error -synctex=1 -shell-escape CS59110440259.tex
bibtex CS59110440259.aux
xelatex -interaction=nonstopmode -file-line-error -synctex=1 -shell-escape CS59110440259.tex
xelatex -interaction=nonstopmode -file-line-error -synctex=1 -shell-escape CS59110440259.tex
latexmk -g -f CS5811400000.tex
##### OR
# xelatex -interaction=nonstopmode -file-line-error -synctex=1 MathCS-tutorial.tex
# bibtex MathCS-tutorial.aux
# xelatex -interaction=nonstopmode -file-line-error -synctex=1 MathCS-tutorial.tex
# xelatex -interaction=nonstopmode -file-line-error -synctex=1 MathCS-tutorial.tex
@internet{angular,
author = {Suratin Pattanawongthai},
title = {Angular คืออะไร ทำความรู้จัก และวิธีใช้งาน},
date = {6},
month = {เมษายน},
year = {2562},
url = {http://www.helloho.me/getting-started-with-angular/},
}
@internet{angular1,
author = {Kritsada L.},
title = {การใช้งาน Angular CLI พื้นฐาน (ตอนที่ 1)},
date = {6},
month = {เมษายน},
year = {2562},
url = {https://medium.com/open-source-technology/การใช้งาน-angular-cli-พื้นฐาน-ตอน-1-168af8ad10b0},
}
@internet{nodejs,
author = {Chai Phonbopit.},
title = {Node.js คืออะไร ? + เริ่มต้นใช้งาน Node.js},
date = {6},
month = {เมษายน},
year = {2562},
url = {https://1th.me/24MG},
}
@internet{typescript,
author = {Nuttavut Thongjor},
title = {TypeScript คืออะไร? เรียนรู้ชนิดข้อมูลพื้นฐานของ TypeScript},
date = {6},
month = {เมษายน},
year = {2562},
url = {https://www.babelcoder.com/blog/posts/typescript-data-types},
}
@internet{vscode,
author = {mindphp},
title = {รู้จักกับ Visual Studio Code (วิชวล สตูดิโอ โค้ด) โปรแกรมฟรีจากค่ายไมโครซอฟท์},
date = {6},
month = {เมษายน},
year = {2562},
url = {https://www.mindphp.com/บทความ/microsoft/4829-visual-studio-code.html},
}
@internet{maps,
author = {thaicreate},
title = {ตอนที่ 1 : Google Maps API (JavaScript) เบื้องต้นกับการใช้งานแผนที่บนของกูเกิลแมพ (Step by Step)},
date = {6},
month = {เมษายน},
year = {2562},
url = {https://www.thaicreate.com/tutorial/google-maps-javascript-api.html},
}
@internet{tensorflow,
author = {tensorflow},
title = {Tensorflow.js},
date = {6},
month = {เมษายน},
year = {2562},
url = {https://www.tensorflow.org/js},
}
@internet{machinelearning,
author = {bossup},
title = {Machine learning คืออะไร?},
date = {18},
month = {เมษายน},
year = {2562},
url = {http://www.bossup.co.th/site/innovation/machine-learning-คืออะไร/},
}
@internet{neuralnetwork,
author = {coladev},
title = {สรุปแนวคิด Neural Network แบบไม่มี Math},
date = {18},
month = {เมษายน},
year = {2562},
url = {https://coladev.com/machine-learning/neural-network/2017/02/22/neural-network-basic},
}
@internet{deeplearning,
author = {Athiwat},
title = {Deep Learning คืออะไร},
date = {18},
month = {เมษายน},
year = {2562},
url = {https://medium.com/@athivvat/deep-leaning-คืออะไร-785e16d01773},
}
@internet{wongnai,
author = {wongnai},
title = {Wongnai},
date = {6},
month = {เมษายน},
year = {2562},
url = {https://www.wongnai.com/about},
}
@internet{architecture,
author = {{Kunchit Phiu-Nual.}},
title = {ความหมายและความสำคัญของ System Architecture},
date = {ุ6},
month = {เมษายน},
year = {2562},
url = {https://goo.gl/6ZhGQo},
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Author: Warawoot Pacharoen
%% Date: April 13, 2018
%% Email: warawoot.p@ubu.ac.th, wpacharoen@gmail.com
%%
%% This class file is extended from chula.cls by Dr.Nattee Niparnan.
%%
%% You can freely modified this file.
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Author: Supasate Choochaisri
%% Date: April 19, 2012
%% Email: supasate.c@gmail.com, supasate@larngeartech.com
%%
%% This class file is extended from chula.cls by Dr.Nattee Niparnan.
%% The chula.cls by Dr.Nattee is loosely based on the chula.sty package
%% originally written by Chatchawit Aporntewan. The chula.sty was
%% then undergone many minor changes by Nattee Niparnan, Teerayut
%% Hiruntaraporn, and Mahisorn Wongphati. Then, Nattee rewrote the
%% style file as a class file and provides several option making the
%% class to be more general. Finally, Supasate modified the class
%% file to be used with XeTeX and to comply with the regulation in
%% acedemic year 2554.
%%
%% You can freely modified this file.
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Changelog
%% August 21, 2015: Dittaya Wanvarie
%% - Move fontspec font settings to AtEndOfClass to avoid problem in Windows TeX
%% July 7, 2015: Dittaya Wanvarie
%% - Delete unnecessary comments
%% - Add more comments
%% - Add standard fonts for English similar to IEEEtran template
%% - Correct errors in English template
%% - Re-define bibliography to use \normalfont
%% - Add natbib required pacakge
%% - Define \thalph order (ก, ข, ค, ง,...0) for appendices
%% - Add setspace require package and force doublespacing
%% June 11, 2015: Dittaya Wanvarie
%% - Move font settings to AtEndOfClass
%% - Change Thai font scale to match roman uppercase character
%% - Add 12pt option to base report class
%% - Add \@ThaiBookTitle to all Co-advisor block
%% - Change tabular settings in TH/EN sign pages
%% - Add array package required for tabular
%% - Fix bugs in co-advisor position in abstract pages
%% June 9, 2015: Dittaya Wanvarie
%% - Change the width sign/department dotted line in TH/EN abstract page to fit "Mathematics and Computer Science"
%% July 21, 2014: Boonyarit Intiyot
%% - Add option [Latin,Thai] to ucharclasses
%% - Add \defaultfontfeatures{Mapping=tex-text}
%% - To avoid problem with Miktex for Windows:
%% -Move \newfontfamily\thaifont[Scale=MatchLowercase,Mapping=tex-text]{TH Sarabun New:script=thai} to the end of file
%% -Move \setTransitionTo{Thai}{\fontspec[Scale=MatchLowercase,Mapping=tex-text]{TH Sarabun New}} to the end of file and change it to \setTransitionTo{Thai}{\thaifont}
%% -Add \setTransitionFrom{Thai}{\normalfont} at the end of file
%%
%% July 6, 2014: Dittaya Wanvarie
%% - Add \defaultfontfeatures{Mapping=tex-text}
%% - Change co-advisor parameter in the English abstract page to uppercase "except
%% "Ph.D"
%% - Add subjID for course report in ugrad
%% - Add ugrad option for senior project report
%% - Add "appendicesname" and "appendicespage" in ThaiCaption
%% May 3, 2012 : Supasate Choochaisri
%% - Add a parameter to handle the uppercase of advisor's title except "Ph.D."
%% - Rename chula_nat.bst to chulanat.bst to make it more compatible to LaTeX
%% compiler.
%% - Add a "numappendices" parameter to handle choosing "Appendix" or "Appendices"
%% - Fix indentation of the Thai abstract page.
%% - Modify the bibtex style file (chulanat.bst) to handle Chula bibliography
%% format.
%%
%% Apr 19, 2012 : Supasate Choochaisri
%% - Use XeTeX to support UTF-8 and OpenType font.
%% - Use ucharclasses package to automatically switch language without
%% explicitly issuing switch command.
%% - Capitalize Advisor name at the English abstract page.
%% - Add option for Advisor/Co-Advisor name with abbreviated title to be used
%% at the English abstract page.
%% - Write Deparment, Field of Study, and Academic year on dotted underlines
%% at the abstract page.
%% - Add semi-colon after Academic year at the English abstract page.
%% - Move the word "Fulfillment" to the second line at the Approval page.
%% - Adjust top margin to 1.5", bottom margin to 1.0" with geometry package.
%% - Add line break for a long thesis title at the cover page.
%%
%% Feb 25, 2007: Nattee Niparnan
%% - Change heading mechanism.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Identification %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{ubu}[2018/04/13]
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Initial Code %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\RequirePackage{calc}
\RequirePackage{fontspec}
\RequirePackage{xunicode}
\RequirePackage{xltxtra}
\RequirePackage{polyglossia}
\RequirePackage{ragged2e}
\RequirePackage{tikz}
\RequirePackage{geometry}
\RequirePackage{afterpage}
\RequirePackage{array}
\RequirePackage[Latin,Thai]{ucharclasses}
\RequirePackage{url} % for breaking urls (use \url{http://www.example.com})
\RequirePackage{breakcites} % for breaking long citations
\RequirePackage[normalem]{ulem} % for underlineing Journal name in references
\RequirePackage{setspace} % for double spacing
\setdefaultlanguage{english}
\setotherlanguage{thai}
\setTransitionTo{Thai}{\thaifont\doublespacing}
\setTransitionFrom{Thai}{\normalfont\doublespacing}
\XeTeXlinebreaklocale "th_TH"
\XeTeXlinebreakskip = 0pt plus 1pt
\newif\if@doctor
\newif\if@master
\newif\if@ugrad
\newif\if@coadvisor
\newif\if@thaithesis
\@coadvisorfalse
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Option Declaration %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\@ugradtrue
\DeclareOption{doctor} {
\@doctortrue
\@masterfalse
\@ugradfalse
} \DeclareOption{master} {
\@doctorfalse
\@mastertrue
\@ugradfalse
} \DeclareOption{ugrad} {
\@doctorfalse
\@masterfalse
} \DeclareOption{coadvisor} {
\@coadvisortrue
} \DeclareOption{thaithesis} {
\@thaithesistrue
} \DeclareOption{engthesis} {
\@thaithesisfalse
}
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{report}} % pass any unknown option to the report class
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Option Execution %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\ExecuteOptions{ugrad,thaithesis}
\ProcessOptions
%%%%%%%%%%%%%%%%%%%%%%%%%%%% Class & Package Loading %%%%%%%%%%%%%%%%%%%%%%%%%%
\LoadClass[a4paper,12pt]{report} % this class is based on the report class
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Main Code %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\AtEndOfClass{
% Add natbib after set all other parameters
%\RequirePackage[round,semicolon]{natbib} % for bibliography sylte
\RequirePackage[square,comma,numbers]{natbib}
\renewenvironment{thebibliography}[1]{%
\thereferences
\bibsection\parindent \z@\bibpreamble\bibfont\list
{\@biblabel{\arabic{NAT@ctr}}}{\@bibsetup{#1}%
\setcounter{NAT@ctr}{0}}%
\normalfont
\ifNAT@openbib
\renewcommand\newblock{\par}
\else
\renewcommand\newblock{\hskip .11em \@plus.33em \@minus.07em}%
\fi
\sloppy\clubpenalty4000\widowpenalty4000
\sfcode`\.=1000\relax
\let\citeN\cite \let\shortcite\cite
\let\citeasnoun\cite
}{\def\@noitemerr{%
\PackageWarning{natbib}
{Empty `thebibliography' environment}}%
\endlist\vskip-\lastskip
}
%% English fonts are Times, Helvetica, Courier, according to IEEEtran template
\defaultfontfeatures{Mapping=tex-text}
\setmainfont{TeX Gyre Termes} % Free Times
\setsansfont{TeX Gyre Heros} % Free Helvetica
\setmonofont{TeX Gyre Cursor} % Free Courier
% Use TH Sarabun New for Thai as it is standard font for Thai formal documents
\newfontfamily{\thaifont}[Scale=MatchUppercase,Mapping=tex-text]{TH Sarabun New:script=thai}
\doublespacing
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% define Thai alphabet sequence
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\def\thalph#1{\expandafter\@thalph\csname c@#1\endcsname}
\def\@thalph#1{%
\ifcase#1\or\or\or\or\or\or\or\or\or
\or\or\or\or\or\or\or\or\or\or\or\or\or\or
\or\or\or\or\or\or\or\or\or\or\or\or\or\or
\or\or\or\or\or\or\else\xpg@ill@value{#1}{@thaialph}\fi}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% localize the strings (xxxxname )
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand\fillin[1][3cm]{\makebox[#1]{\dotfill}}
\newcommand\selectthesislang{
\if@thaithesis
\selectlanguage{thai}
\else
\selectlanguage{english}
\fi
}
%% string setting
\if@doctor
\newcommand\@ThaiDegreeType{ปริญญาดุษฎีบัณฑิต}
\newcommand\@EnglishDegreeType{Doctoral Degree}
\newcommand\@ThaiBookTitle{วิทยานิพนธ์}
\newcommand\@EnglishBookTitle{Dissertation}
\else
\if@master
\newcommand\@ThaiDegreeType{ปริญญามหาบัณฑิต}
\newcommand\@EnglishDegreeType{Master's Degree}
\newcommand\@ThaiBookTitle{วิทยานิพนธ์}
\newcommand\@EnglishBookTitle{Thesis}
\else
\newcommand\@ThaiDegreeType{ปริญญาบัณฑิต}
\newcommand\@EnglishDegreeType{Bachelor's Degree}
\newcommand\@ThaiBookTitle{โครงงาน}
\newcommand\@EnglishBookTitle{Project}
\fi
\fi
\if@thaithesis
%% my new string
\newcommand{\@AcknowledgementsString}{กิตติกรรมประกาศ}
\if@ugrad
\newcommand{\@BiographyString}{ประวัติผู้เขียน}
\else
\newcommand{\@BiographyString}{ประวัติผู้เขียนวิทยานิพนธ์}
\fi
\newcommand{\@AbstractThaiString}{บทคัดย่อภาษาไทย}
\newcommand{\@AbstractEnglishString}{บทคัดย่อภาษาอังกฤษ}
\newcommand{\@PageString}{หน้า}
\newcommand{\@ThaiFont}{\normalfont}
%%string already defined in babel (some are not used in this class, but I add it anyway
\renewcommand\captionsthai{%
\def\prefacename{คำนำ}%
\def\refname{หนังสืออ้างอิง}%
\def\abstractname{บทคัดย่อ}%
\def\bibname{บรรณานุกรม}%
\def\chaptername{บทที่}%
\def\appendixname{ภาคผนวก}%
\def\appendicesname{ภาคผนวก}%
\def\appendixpage{ภาคผนวก}%
\def\appendicespage{ภาคผนวก}%
\def\contentsname{สารบัญ}%
\def\listfigurename{สารบัญภาพ}%
\def\listtablename{สารบัญตาราง}%
\def\indexname{ดรรชนี}%
\def\figurename{รูปที่}%
\def\tablename{ตารางที่}%
\def\partname{ภาค}%
\def\enclname{สิ่งที่แนบมาด้วย}%
\def\ccname{สำเนาถึง}%
\def\headtoname{เรียน}%
\def\pagename{หน้า}%
\def\seename{ดู}%
\def\alsoname{ดูเพิ่มเติม}%
\def\proofname{พิสูจน์}%
}
\else
%% my new string
\newcommand{\@AcknowledgementsString}{Acknowledgements}
\newcommand{\@BiographyString}{Biography}
\newcommand{\@AbstractThaiString}{Abstract (Thai)}
\newcommand{\@AbstractEnglishString}{Abstract (English)}
\newcommand{\@PageString}{Page}
\newcommand{\@ThaiFont}{\normalfont}
%%string already defined in babel (some are not used in this class, but I add it anyway
\renewcommand\captionsenglish{%
\def\prefacename{Preface}%
\def\refname{References}%
\def\abstractname{Abstract}%
\def\bibname{References}%
\def\chaptername{Chapter}%
\def\appendixname{Appendix}%
\def\appendicesname{Appendices}%
\def\appendixpage{APPENDIX}%
\def\appendicespage{APPENDICES}%
\def\contentsname{Contents}%
\def\listfigurename{List of Figures}%
\def\listtablename{List of Tables}%
\def\indexname{Index}%
\def\figurename{Figure}%
\def\tablename{Table}%
\def\partname{Part}%
\def\enclname{encl}%
\def\ccname{cc}%
\def\headtoname{To}%
\def\pagename{Page}%
\def\seename{see}%
\def\alsoname{see also}%
\def\proofname{Proof}%
\def\glossaryname{Glossary}%
\def\figurename{Figure}
\def\tablename{Table}
}
\fi
\if@thaithesis
\captionsthai
\else
\captionsenglish
\fi
%% set chapter string
\renewcommand\@chapapp{\chaptername}
%% to write text over dotted line
\newcommand{\udot}[1]{%
\tikz[baseline=(todotted.south)]{
\node[inner sep=0pt,outer sep=0pt] (todotted) {#1};
\draw[loosely dotted, thick] (todotted.south west) -- (todotted.south east);
}%
}%
%% to choose the word between Appendix or Appendices
\newcommand{\numappendices}[1]{
\newcommand{\@numappendices}{#1}
}
%% additional command for setting the first few pages of the thesis
%% please refer to the accompanying bare_thesis.tex for the example
%% usage of these command.
\newcommand{\authortitle}[2]{ % define Title of Author
\newcommand{\@ThaiAuthorTitle}{#1}
\newcommand{\@EnglishAuthorTitle}{#2}
}
\newcommand{\thesisauthor}[2]{ % define author
\newcommand{\@ThaiAuthor}{#1}
\newcommand{\@EnglishAuthor}{#2}
\newcommand{\@EnglishAuthorUP}{\uppercase{#2}}
}
\newcommand{\thesistitle}[2]{ % define Thesis' title
\newcommand{\@ThaiTitle}{#1}
\newcommand{\@EnglishTitle}{#2}
}
\newcommand{\advisor}[4]{ % define Advisor
\newcommand{\@ThaiAdvisor}{#1}
\newcommand{\@ThaiAdvisorShort}{#2}
\newcommand{\@EnglishAdvisor}{#3}
\newcommand{\@EnglishAdvisorUP}{\uppercase{#3}}
\newcommand{\@EnglishAdvisorShort}{#4}
\newcommand{\@EnglishAdvisorShortUP}{\uppercase{#4}}
}
\newcommand{\coadvisor}[4]{ % define co-author (auto include coadvision option)
\newcommand{\@ThaiCoAdvisor}{#1}
\newcommand{\@ThaiCoAdvisorShort}{#2}
\newcommand{\@EnglishCoAdvisor}{#3}
\newcommand{\@EnglishCoAdvisorUP}{\uppercase{#3}}
\newcommand{\@EnglishCoAdvisorShort}{#4}
\newcommand{\@EnglishCoAdvisorShortUP}{\uppercase{#4}}
\@coadvisortrue
}
\newcommand{\faculty}[2]{ % define faculty
\newcommand{\@ThaiFaculty}{#1}
\newcommand{\@EnglishFaculty}{#2}
}
\newcommand{\department}[2]{ % define department
\newcommand{\@ThaiDept}{#1}
\newcommand{\@EnglishDept}{#2}
}
\newcommand{\fieldofstudy}[2]{ % define field of study
\newcommand{\@ThaiFieldOfStudy}{#1}
\newcommand{\@EnglishFieldOfStudy}{#2}
\newcommand{\@EnglishFieldOfStudyUP}{\uppercase{#2}}
}
\newcommand{\degree}[2]{ % define degree name
\newcommand{\@ThaiDegree}{#1}
\newcommand{\@EnglishDegree}{#2}
}
\newcommand{\academicyear}[1]{ % define academic year
\newcounter{AcadYear}
\setcounter{AcadYear}{#1}
\newcommand{\@ThaiAcademicYear}{\theAcadYear}
\newcounter{EngAcadYear}
\setcounter{EngAcadYear}{\value{AcadYear}-543}
\newcommand{\@EnglishAcademicYear}{\theEngAcadYear}
}
\newcommand{\deanname}[2]{ % define name of the dean
\newcommand{\@ThaiDeanName}{#1}
\newcommand{\@EnglishDeanName}{#2}
}
\newcommand{\subjID}[1]{
\newcommand{\@subjID}{#1}
}
\newcommand{\subjName}[2]{
\newcommand{\@ThaiSubjName}{#1}
\newcommand{\@EnglishSubjName}{#2}
}
\newcommand{\keywords}[1]{ % define keywords
\newcommand{\@Keywords}{\uppercase{#1}}
}
\newcommand{\authorid}[1]{ % define student ID of the author
\newcommand{\@AuthorID}{#1}
}
\newcommand{\committee}[1]{ % define commitee
\newcommand{\@Committee}{
\if@thaithesis \fi
#1
}
}
%% additional counter, length, reference, etc
\newcounter{subsubsubsection}[subsubsection]
\setcounter{subsubsection}{0} % this class allow subsubsubsection
\newcounter{bib} \setcounter{bib}{0}
\newcounter{TotalPage} \setcounter{TotalPage}{0}
\newcounter{isAppendiced} \setcounter{isAppendiced}{0}
\setcounter{tocdepth}{5}
\setcounter{secnumdepth}{5}
\newlength{\pageleft} % for the remaining space of the page
\renewcommand{\thechapter}{\@ThaiFont\@arabic\c@chapter} % \ref{} in chapter
\renewcommand{\thesection}{\@ThaiFont\thechapter.\@arabic\c@section} % \ref{} in section
\renewcommand{\thesubsection}{\@ThaiFont\thesection.\@arabic\c@subsection} % \ref{} in subsection
\renewcommand{\thesubsubsection}{\@ThaiFont\thesubsection.\@arabic\c@subsubsection} % \ref{} in subsubsection
\renewcommand{\thesubsubsubsection}{\@ThaiFont\thesubsubsection.\@arabic\c@subsubsubsection}% \ref{} in subsubsubsection
\renewcommand{\@cite}[1]{[#1]}
%\renewcommand{\@biblabel}[1]{#1.} %%Change [1] to 1.
%% command for appendix
\newcommand\startappendix{
%readjust toc length
\addtocontents{toc}{\protect\vspace*{0.5cm}}
\addtocontents{toc}{\protect\setlength{\tocindentchap}{1.4em}}
\addtocontents{toc}{\protect\gentocwidthparam} %regen according to new chapter indent
\addtocontents{toc}{\protect{\setlength{\tocnwidthchap}{10.0em}}} %set the nwidth of chapter AFTER generate (to accommodate \appendixname)
\par
\setcounter{chapter}{0}%
\setcounter{section}{0}%
\setcounter{isAppendiced}{1}%
\renewcommand\@chapapp{\appendixname}%
\if@thaithesis
\renewcommand{\thechapter}{\@thaialph\c@chapter}
\else
\renewcommand{\thechapter}{\@Alph\c@chapter}
\fi
\newpage
\thispagestyle{empty}
\centerline{~}
\vfill
\ifnum \@numappendices > 1
\centerline{\Large \appendicespage}
\else
\centerline{\Large \appendixpage}
\fi
\vfill
\centerline{~}
}
%%%%%%%%%%%%%%%
% page layout %
%%%%%%%%%%%%%%%
\setlength{\textheight}{25.00cm} % text-area height
\geometry{paperwidth=8.27in,paperheight=11.69in,top=1.5in,bottom=1in,left=1.5in,right=1.0in} % set paper size and page margin
\setlength{\headheight}{0.00cm} %
\setlength{\headsep}{1.00cm} %
\setlength{\marginparsep}{0.00cm} %
\setlength{\marginparwidth}{0.00cm} %
\setlength{\footskip}{0.00cm} %
\setlength{\parindent}{1.00cm} % paragraph indent
\setlength{\parskip}{0.20cm} % distance between paragraphs
%%%%%%%%%%%%%%%%%%
% page numbering %
%%%%%%%%%%%%%%%%%%
\def\ps@headings{\def\@oddhead{{\slshape\rightmark}\hfil\if@thaithesis\fi\thepage}}
\pagestyle{myheadings}
\if@thaithesis
\pagenumbering{thaialph}
\else
\pagenumbering{roman}
\fi
%%%%%%%%%%%%%%%%%%
% small commands %
%%%%%%%%%%%%%%%%%%
\renewcommand\chapter{
\selectthesislang
\if@openright\cleardoublepage\else\clearpage\fi
\thispagestyle{empty}%
\global\@topnum\z@ % Prevents figures from going at top of page.
\@afterindenttrue
\ifnum \c@chapter = 0
\ifnum \c@isAppendiced = 0
\pagenumbering{arabic}
\toccont
\addtocontents{toc}{\protect\leftline{\bfseries \chaptername}}
\else
\toccont
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\ifnum \@numappendices > 1
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries \appendicesname}{\@ThaiFont\thepage}}
\else
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries \appendixname}{\@ThaiFont\thepage}}
\fi
\fi
\fi
\ifnum \c@isAppendiced = 1
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\fi
\secdef\@chapter\@schapter
}
\def\@chapter[#1]#2{
\refstepcounter{chapter}
\toccont
\addtocontents{toc}{\protect\vspace*{0cm}}
\ifnum \c@isAppendiced = 0
\addcontentsline{toc}{chapter}{\protect\numberline{\thechapter}#1}
\else
\addcontentsline{toc}{chapter}{\appendixname~\protect\numberline{\thechapter}#1}
\fi
\@makechapterhead{#2}
\@afterheading
\indent
}
\def\@schapter#1{
{
\parindent \z@ \centering % zero indent (\z@ = 0pt}
\bfseries\Large #1 \par\nobreak
}
\@afterheading
\indent
}
\def\@makechapterhead#1{%
{
\parindent \z@ \centering
\bfseries\Large \MakeUppercase{\@chapapp}
\ifnum \c@isAppendiced = 0 % test if this chapter is an appendix
\if@thaithesis
\@ThaiFont\arabic{chapter}
\else
\Roman{chapter}
\fi
\else
\if@thaithesis
\thalph{chapter}
\else
\Alph{chapter}
\fi
\fi
\par\nobreak
\bfseries\Large \uppercase{#1} \par\nobreak
}
}
\renewcommand{\section}{
\toccont
\addtocontents{toc}{\protect\vspace*{-0.1cm}\@ThaiFont}
\@startsection{section}{2}{0cm}{0cm}{0.001cm}{\bfseries\large}
}
\renewcommand{\subsection}{
\toccont
\addtocontents{toc}{\protect\vspace*{-0.1cm}\@ThaiFont}
\@startsection{subsection}{3}{0cm}{0cm}{0.001cm}{\bfseries}
}
\renewcommand{\subsubsection}{
\toccont
\addtocontents{toc}{\protect\vspace*{-0.1cm}\@ThaiFont}
\@startsection{subsubsection}{4}{0cm}{0cm}{0.001cm}{\bfseries}
}
\newcommand{\subsubsubsection} {
\toccont
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\@startsection{subsubsubsection}{5}{0cm}{0cm}{0.001cm}{\bfseries}
}
%%%%%%%%%%%%
% contents %
%%%%%%%%%%%%
\renewcommand\@pnumwidth{0.5cm} % toc's parameter
\renewcommand\@tocrmarg{1.5cm} % toc's parameter
\renewcommand\@dotsep{4} % toc's parameter
\addtocontents{toc}{\protect\rightline{\pagename}}
\newlength{\tocindentchap}
\newlength{\tocnwidthchap}
\newlength{\tocindentsection}
\newlength{\tocnwidthsection}
\newlength{\tocindentsubsection}
\newlength{\tocnwidthsubsection}
\newlength{\tocindentsubsubsection}
\newlength{\tocnwidthsubsubsection}
\newlength{\tocindentsubsubsubsection}
\newlength{\tocnwidthsubsubsubsection}
\newlength{\tocnwidthperstep}
\newlength{\tocnwidthoffset}
\setlength{\tocnwidthperstep}{0.8em}
\setlength{\tocnwidthoffset}{1.4em}
\setlength{\tocindentchap}{0.0em}
\setlength{\tocnwidthchap}{\tocnwidthoffset}
\newcommand{\gentocwidthparam}{
\setlength{\tocindentsection}{\tocindentchap + \tocnwidthchap}
\setlength{\tocnwidthsection}{\tocnwidthoffset + \tocnwidthperstep * 1}
\setlength{\tocindentsubsection}{\tocindentsection + \tocnwidthsection}
\setlength{\tocnwidthsubsection}{\tocnwidthoffset + \tocnwidthperstep * 2}
\setlength{\tocindentsubsubsection}{\tocindentsubsection + \tocnwidthsubsection}
\setlength{\tocnwidthsubsubsection}{\tocnwidthoffset + \tocnwidthperstep * 3}
\setlength{\tocindentsubsubsubsection}{\tocindentsubsubsection + \tocnwidthsubsubsection}
\setlength{\tocnwidthsubsubsubsection}{\tocnwidthoffset + \tocnwidthperstep * 3}
}
\gentocwidthparam
\newcommand{\l@nchapter}{\@dottedtocline{0}{0cm}{0cm}}
\renewcommand{\l@chapter}[2]{\@dottedtocline{0}{\tocindentchap}{\tocnwidthchap}{\bfseries #1}{\bfseries #2}}
\renewcommand{\l@section}{\@dottedtocline{1}{\tocindentsection}{\tocnwidthsection}}
\renewcommand{\l@subsection}{\@dottedtocline{2}{\tocindentsubsection}{\tocnwidthsubsection}}
\renewcommand{\l@subsubsection}{\@dottedtocline{3}{\tocindentsubsubsection}{\tocnwidthsubsubsection}}
\newcommand{\l@subsubsubsection}{\@dottedtocline{4}{\tocindentsubsubsubsection}{\tocnwidthsubsubsubsection}}
\renewcommand{\l@table}{\@dottedtocline{0}{0cm}{1.6em}}
\renewcommand{\l@figure}{\@dottedtocline{0}{0cm}{2.4em}}
\renewcommand{\tableofcontents}{
\selectthesislang
\thispagestyle{empty}
\toccont
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries \contentsname}{\@ThaiFont\thepage}}
\centerline{\bfseries\Large \contentsname}
\@starttoc{toc}
}
\newcommand{\toccont}{
\addtocontents{toc}{
\protect\setlength{\protect\pageleft}{\protect\textheight - \protect\pagetotal}
\protect\ifdim \protect\pageleft < 1cm
\protect\newpage
\protect\centerline{\bfseries\Large }
{\hspace{-1cm}\chaptername\hfill\pagename\hspace{-0.1cm}}
\protect\fi
}
}
%%%%%%%%%%%%%%%%%
% list of table %
%%%%%%%%%%%%%%%%%
\setlength{\arraycolsep}{5\p@} % tabular's parameter
\setlength{\tabcolsep}{6\p@} % tabular's parameter
\setlength{\arrayrulewidth}{.4\p@} % tabular's parameter
\setlength{\doublerulesep}{2\p@} % tabular's parameter
\renewcommand{\arraystretch}{1.0} % tabular's parameter
\renewcommand{\thetable}{\ifnum\c@chapter>\z@\thechapter.\fi\@arabic\c@table}
\def\fps@table{tbp}
\def\ftype@table{2}
\def\ext@table{lot}
\def\fnum@table{\tablename~\thetable}
\renewenvironment{table}{
\@float{table}
}{
\lotcont
\end@float
}
\setlength{\abovecaptionskip}{10\p@}
\setlength{\belowcaptionskip}{10\p@}
\long\def\@makecaption#1#2{
\vskip\abovecaptionskip
\sbox\@tempboxa{#1: #2}
\ifdim \wd\@tempboxa > \hsize
#1: #2\par
\else
\global \@minipagefalse
\hb@xt@\hsize{\hfil\box\@tempboxa\hfil}
\fi
\vskip\belowcaptionskip}
\renewcommand{\listoftables}{
\toccont
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries \listtablename}{\@ThaiFont\thepage}}
\centerline{\bfseries\Large \listtablename}
\@starttoc{lot}
\addtocontents{lot}{\hspace{-1cm}\tablename\hfill\pagename\hspace{-0.1cm}}
\addtocontents{lot}{\protect\setlength{\parskip}{0.0cm}} % distance between paragraphs
}
\newcommand{\lotcont}{
\addtocontents{lot}{
\protect\setlength{\protect\pageleft}{\protect\textheight - \protect\pagetotal}
\protect\ifdim \protect\pageleft < 1cm
\protect\newpage
{\hspace{-1cm}\tablename\hfill\pagename\hspace{-0.1cm}}
\protect\fi
}
}
%%%%%%%%%%%%%%%%%%%
% list of figures %
%%%%%%%%%%%%%%%%%%%
\renewcommand{\thefigure}{\ifnum\c@chapter>\z@\thechapter.\fi\@arabic\c@figure}
\def\fps@figure{tbp}
\def\ftype@figure{1}
\def\ext@figure{lof}
\def\fnum@figure{\figurename~\thefigure}
\renewenvironment{figure}{
\@float{figure}
}{
\lofcont
\end@float
}
\renewcommand{\listoffigures}{
\toccont
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries \listfigurename}{\@ThaiFont\thepage}}
\addtocontents{toc}{\protect\vspace*{0.5cm}}
\centerline{\bfseries\Large \listfigurename}
\@starttoc{lof}
\addtocontents{lof}{\hspace{-1cm}\figurename\hfill\pagename\hspace{-0.1cm}}
%% my new attempt
\addtocontents{lof}{\protect\setlength{\parskip}{0.0cm}} % distance between paragraphs
\addtocontents{lof}{\protect\thispagestyle{myheadings}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% save number of roman pages %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\addtocounter{TotalPage}{\arabic{page}}
}
\newcommand{\lofcont}{
\addtocontents{lof}{
\protect\setlength{\protect\pageleft}{\protect\textheight - \protect\pagetotal}
\protect\ifdim \protect\pageleft < 1cm
\protect\newpage
{\hspace{-1cm}\figurename\hfill\pagename\hspace{-0.1cm}}
\protect\fi
}
}
%%%%%%%%%%%%
% equation %
%%%%%%%%%%%%
\@addtoreset{equation}{chapter}
\renewcommand{\theequation}{\ifnum\c@chapter>\z@\thechapter.\fi\@arabic\c@equation}
%%%%%%%%%%%%%
% emphasize %
%%%%%%%%%%%%%
\newcommand\e[1]{{\normalem \emph{#1}}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Front matter commands
%%
%% These commands generate the first few pages of the thesis, such as
%% Thai Title, English Title, Committee Page, Abstract,
%% Acknowledgement, etc.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\newcommand{\makethaicover}{
%\fontsize{11}{15}\selectfont
\large
\thispagestyle{empty}
\centerline{ \begin{tabular}{p{14cm}}\centering\@ThaiTitle\end{tabular} }
\centerline{ \begin{tabular}{p{14cm}}\centering\@EnglishTitle\end{tabular}}
\vfill
\centerline{\@ThaiAuthorTitle\@ThaiAuthor}
\vspace{1cm}
\vfill
\begin{center}
{\@ThaiBookTitle}นี้เป็นส่วนหนึ่งของการศึกษา \\
หลักสูตร{\@ThaiDegree} สาขาวิชา{\@ThaiFieldOfStudy} \\
ภาควิชา{\@ThaiDept} คณะ{\@ThaiFaculty} \\
มหาวิทยาลัยอุบลราชธานี \\
ปีการศึกษา {\begin{english}\@ThaiAcademicYear\end{english}}\\
ลิขสิทธิ์ของมหาวิทยาลัยอุบลราชธานี
\end{center}
\normalsize
}
\newcommand{\makeenglishcover}{
\fontsize{11}{15}\selectfont
\thispagestyle{empty}
\centerline{\begin{tabular}{p{14cm}}\centering\@EnglishTitle\end{tabular}}
\vfill
\centerline{\@EnglishAuthorTitle~\@EnglishAuthor}
\vfill
\begin{center}
A {\@EnglishBookTitle} Submitted in Partial Fulfillment of the Requirements \\
for the Degree of {\@EnglishDegree} Program in {\@EnglishFieldOfStudy} \\
Department of {\@EnglishDept} \\
Faculty of {\@EnglishFaculty} \\
Ubon Ratchathani University \\
Academic Year {\@EnglishAcademicYear} \\
Copyright of Ubon Ratchathani University
\end{center}
}
\newcommand{\ThaiCommittee}[1]{
\fontsize{11}{13.2}\selectfont
\thispagestyle{empty}
\noindent
\begin{tabular}{@{}p{3.8cm}p{0.5cm}p{9.2cm}}
{{\@ThaiBookTitle}} & : & \@ThaiTitle \\
{} & {} & \@EnglishTitle \\
{โดย} & : & \@ThaiAuthorTitle\@ThaiAuthor \\
{อาจารย์ที่ปรึกษา} & : & \@ThaiAdvisorShort \\
\if@coadvisor
{อาจารย์ที่ปรึกษาร่วม} & : & \@ThaiCoAdvisor \\
\fi
{ระดับการศึกษา} & : & {\@ThaiDegree} สาขาวิชา{\@ThaiFieldOfStudy} \\
{ปีการศึกษา} & : & \@ThaiAcademicYear \\
\end{tabular}
\vspace*{0.4cm}
\hrule width 15 cm height 0.0 cm depth 0.025 cm
\begin{center}
{\bfseries
ได้รับการพิจารณาให้เป็นส่วนหนึ่งของการศึกษา \\
ตามหลักสูตร{\@ThaiDegree} สาขา{\@ThaiFieldOfStudy} \\
}
\end{center}
%\if@ugrad {ภาควิชา\@ThaiDept} \fi {คณะ\@ThaiFaculty} {มหาวิทยาลัยอุบลราชธานี} {อนุมัติให้นับ{\@ThaiBookTitle}ฉบับนี้เป็นส่วนหนี่งของการศึกษาตามหลักสูตร{\@ThaiDegreeType}} \if@ugrad ในรายวิชา {\@subjID} {\@ThaiSubjName} \fi
% \vspace*{0.3cm}
\noindent
\hspace*{1.5cm} {\bfseries คณะกรรมการสอบประเมินความรู้โครงงานคอมพิวเตอร์}
\noindent
\begin{tabular}{p{3cm}>{\centering\arraybackslash}p{6cm}l}
\@Committee
\end{tabular}
\noindent
\begin{tabular}{p{3cm}>{\centering\arraybackslash}p{6cm}l}
\hspace*{0.5cm} & \hspace*{8.0cm} & \\
& \dotfill & {\if@ugrad{หัวหน้าภาควิชา}\else{คณบดีคณะ{\@ThaiFaculty}}\fi} \\
& {(\@ThaiDeanName)}
\end{tabular}
\noindent
\begin{tabular}{p{3cm}>{\centering\arraybackslash}p{6cm}l}
\hspace*{0.5cm} & \hspace*{8.0cm} & \\
& วันที่\fillin[1cm]/\fillin[1cm]/\fillin[1cm] & {}
\end{tabular}
}
\newcommand{\EnglishCommittee}{
\fontsize{11}{13.2}\selectfont
\setlength{\textwidth}{16.50cm}
\thispagestyle{empty}
\noindent
\begin{tabular}{p{3cm}p{11cm}}
\@EnglishBookTitle Title & \@EnglishTitle \\
By & \@EnglishAuthorTitle\@EnglishAuthor \\
Field of Study & \@EnglishFieldOfStudy \\
\@EnglishBookTitle~Advisor & \@EnglishAdvisor \\
\if@coadvisor
\@EnglishBookTitle~Co-advisor & \@EnglishCoAdvisor \\
\fi
\end{tabular}
\vspace*{0.4cm}
\hrule width 15 cm height 0.0 cm depth 0.025 cm
Accepted by the \if@ugrad Department of \@EnglishDept\fi Faculty of \@EnglishFaculty, Ubon Ratchathani University in Partial Fulfillment of the Requirements for the \@EnglishDegreeType \if@ugrad in {\@subjID} {\@EnglishSubjName}\fi
\noindent
\begin{tabular}{p{3cm}p{6cm}>{\raggedright\arraybackslash}p{4.5cm}}
\hspace*{0.5cm} & \hspace*{8.0cm} & \\
& \dotfill & Dean of the {\if@ugrad{Department of \@EnglishDept}\else{Faculty of \@EnglishFaculty}\fi} \\
& \multicolumn{2}{l}{(\@EnglishDeanName)}
\end{tabular}
\vspace*{0.4cm}
\noindent
\if@ugrad{PROJECT}\else{THESIS}\fi~COMMITTEE
\noindent
\begin{tabular}{p{3cm}>{\centering\arraybackslash}p{6cm}l}
\@Committee
\end{tabular}
\setlength{\textwidth}{15.00cm}
}
\newcommand{\makecommittee} {
\protect\if@thaithesis
\ThaiCommittee
\protect\else
\EnglishCommittee
\protect\fi
}
\newcommand{\CommitteeBlock}[2] {
\hspace*{0.5cm} & \hspace*{8.0cm} & \\
& \dotfill & {#1} \\
& {(#2)} \\
}
\newcommand{\CommitteeBlockAdvisor} {
\hspace*{0.5cm} & \hspace*{8.0cm} & \\
& \dotfill & {\if@thaithesis {อาจารย์ที่ปรึกษา} \else \@EnglishBookTitle~Advisor \fi} \\
& (\if@thaithesis \@ThaiAdvisorShort \else \@EnglishAdvisor \fi) \\
}
\newcommand{\CommitteeBlockCoAdvisor} {
\if@coadvisor
\hspace*{0.5cm} & \hspace*{8.0cm} & \\
& \dotfill & {\if@thaithesis {อาจารย์ที่ปรึกษาร่วม} \else {\@EnglishBookTitle}~Co-advisor \fi} \\
& (\if@thaithesis \@ThaiCoAdvisorShort \else \@EnglishCoAdvisor~ \fi) \\
\fi
}
%%environment for multiple paragraph page such as abstract and acknowledgement
\newenvironment{thaiabstract}{
\fontsize{11}{15}\selectfont
\thispagestyle{headings}
\toccont
\@ThaiFont
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries\@AbstractThaiString}{\@ThaiFont\thepage}}
\noindent
\begin{tabular}{@{}p{3.8cm}p{0.5cm}p{9.2cm}}
{{\@ThaiBookTitle}} & : & \@ThaiTitle \\
%{} & {} & \@EnglishTitle \\
{โดย} & : & \@ThaiAuthorTitle\@ThaiAuthor \\
{อาจารย์ที่ปรึกษา} & : & \@ThaiAdvisorShort \\
\if@coadvisor
{อาจารย์ที่ปรึกษาร่วม} & : & \@ThaiCoAdvisor \\
\fi
{ระดับการศึกษา} & : & {\@ThaiDegree} สาขาวิชา{\@ThaiFieldOfStudy} \\
{ปีการศึกษา} & : & \@ThaiAcademicYear \\
\end{tabular}
\vspace*{0.4cm}
\hrule width 15 cm height 0.0 cm depth 0.025 cm
\vspace*{0.4cm}
\centerline{\bfseries\Large บทคัดย่อ}
\normalsize
}
\newenvironment{englishabstract}{
\fontsize{11}{15}\selectfont
\thispagestyle{headings}
\toccont
\@ThaiFont
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries\@AbstractEnglishString}{\@ThaiFont\thepage}}
\noindent
\begin{tabular}{@{}p{3.8cm}p{0.5cm}p{9.2cm}}
{Topic} & : & \@EnglishTitle \\
%{} & {} & \@EnglishTitle \\
{Author} & : & \uppercase{\@EnglishAuthorUP} \\
{Advisor} & : & \@EnglishAdvisorShort \\
\if@coadvisor
{Co-advisor} & : & \@EnglishCoAdvisorShort \\
\fi
{Degree} & : & {\@EnglishDegree} ({\@EnglishFieldOfStudy}) \\
{Academic Year} & : & \@EnglishAcademicYear \\
\end{tabular}
\vspace*{0.4cm}
\hrule width 15 cm height 0.0 cm depth 0.025 cm
\vspace*{0.4cm}
\centerline{\bfseries\Large Abstract}
\normalsize
}
\newenvironment{englishabstract2}{
% \selectthesislang
\fontsize{11}{15}\selectfont
\thispagestyle{headings}
\toccont
\@ThaiFont
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries \@AbstractEnglishString}{\@ThaiFont\thepage}}
\noindent
\#\# \@AuthorID : MAJOR \uppercase{\@EnglishFieldOfStudyUP} \\
KEYWORDS: \@KeywordsEnglish
\vspace{-\parskip}
\hangindent = 1.0 cm \hangafter = 1
\uppercase{\@EnglishAuthorUP} : \@EnglishTitle. ADVISOR : \uppercase{\@EnglishAdvisorShort},
{\if@coadvisor \MakeUppercase{\@EnglishBookTitle} COADVISOR : \uppercase{\@EnglishCoAdvisorShort},\fi } \ref{@TheLastPage} pp.
} {
\vfill
\noindent
\begin{tabular}{l@{}p{6.2cm}p{5.6cm}@{}}
& \\
Department &: {\@EnglishDept}\dotfill & Student's Signature \dotfill \\
Field of Study &: {\@EnglishFieldOfStudy}\dotfill & Advisor's Signature \dotfill \\
Academic Year &: {\@EnglishAcademicYear}\dotfill &
\if@coadvisor
Co-advisor's signature \dotfill \\
\fi
\end{tabular}
}
\newenvironment{acknowledgements}{
\selectthesislang
\fontsize{11}{15}
\toccont
\addtocontents{toc}{\protect\vspace*{-0.1cm}}
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries \@AcknowledgementsString}{\@ThaiFont\thepage}}
\centerline{\bfseries\Large \@AcknowledgementsString}
\noindent
\hfill
\hspace*{1.0cm}
\normalfont
}
\newenvironment{biography}{
\selectthesislang
\newpage
\toccont
\addtocontents{toc}{\protect\vspace*{0.5cm}}
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries \@BiographyString}{\@ThaiFont\thepage}}
\centerline{\bfseries\Large \@BiographyString}
} {
%%%%%%%%%%%%%%%%%%%%%
% compute TotalPage %
%%%%%%%%%%%%%%%%%%%%%
\addtocounter{TotalPage}{\arabic{page}}
\addtocounter{TotalPage}{-1}
\refstepcounter{TotalPage}
\label{@TheLastPage}
}
\newcommand\thereferences{
\toccont
\addtocontents{toc}{\protect\vspace*{0.5cm}}
\addtocontents{toc}{\protect\contentsline{nchapter}{\bfseries \bibname}{\@ThaiFont\thepage}\vspace*{0.5cm}}
}
......@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="%PUBLIC_URL%/logo512.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
......
b_beauty_project/public/logo192.png

2.93 KB | W: | H:

b_beauty_project/public/logo192.png

19.2 KB | W: | H:

b_beauty_project/public/logo192.png
b_beauty_project/public/logo192.png
b_beauty_project/public/logo192.png
b_beauty_project/public/logo192.png
  • 2-up
  • Swipe
  • Onion skin
b_beauty_project/public/logo512.png

2.93 KB | W: | H:

b_beauty_project/public/logo512.png

69.2 KB | W: | H:

b_beauty_project/public/logo512.png
b_beauty_project/public/logo512.png
b_beauty_project/public/logo512.png
b_beauty_project/public/logo512.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -3,17 +3,28 @@ import { withRouter } from "react-router-dom";
import compose from "recompose/compose";
import { withStyles } from "@material-ui/core/styles";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";
import IconButton from "@material-ui/core/IconButton";
import {
Button,
Divider,
IconButton,
AppBar,
Toolbar,
Typography,
Link
} from "@material-ui/core";
import AccountCircle from "@material-ui/icons/AccountCircle";
import AddPhotoAlternateIcon from "@material-ui/icons/AddPhotoAlternate";
import MailOutlineIcon from "@material-ui/icons/MailOutline";
import VpnKeyIcon from "@material-ui/icons/VpnKey";
import MenuIcon from "@material-ui/icons/Menu";
import axios from "axios";
const styles = theme => ({
root: {
flexGrow: 1
},
menuButton: {
marginRight: theme.spacing(2)
},
......@@ -21,9 +32,40 @@ const styles = theme => ({
flexGrow: 1
}
});
class AppHeader extends Component {
state = {
user: null
};
// constructor(props) {
// super(props);
// this.state = {
// user: null
// };
// }
componentDidMount = () => {
this.getCurrentUser();
};
componentWillReceiveProps = nextProp => {
this.getCurrentUser();
};
getCurrentUser = async () => {
const token = localStorage.getItem("token");
if (token) {
const user = await axios.get(
"http://localhost:9000/api/auth/currentUser",
{ headers: { token: token } }
);
this.setState({ user: user.data });
}
};
render() {
const { classes } = this.props;
const { user } = this.state;
return (
<div className={classes.root}>
......@@ -37,18 +79,37 @@ class AppHeader extends Component {
Link
href="/"
>
<img src={require('../../image/icon.png') }></img>
<img src={require("../../image/icon.png")}></img>
</IconButton>
<Typography variant="h6" className={classes.title}></Typography>
<Button color="inherit" Link href="/LoginPage">
Login
</Button>
<Button color="inherit" Link href="/RigisterPage">
rigister
</Button>
<Button color="inherit" Link href="/RigisterShopPage">
rigistershop
</Button>
{!user ? (
[
<Button color="inherit" Link href="/LoginPage">
Login
</Button>,
<Button color="inherit" Link href="/RigisterPage">
rigister
</Button>,
<Button color="inherit" Link href="/RigisterShopPage">
rigistershop
</Button>
]
) : (
<div>
{user.name}
<Button
color="inherit"
onClick={() => {
localStorage.removeItem("token");
this.props.history.push("/");
this.setState({ user: null });
}}
>
Logout
</Button>
</div>
)}
</Toolbar>
</AppBar>
</div>
......@@ -56,3 +117,5 @@ class AppHeader extends Component {
}
}
export default compose(withStyles(styles), withRouter)(AppHeader);
// Redux
......@@ -94,7 +94,7 @@ class ResponsiveDrawer extends Component {
color="inherit"
aria-label="menu"
Link
href="/"
href="/BeauticianShopPage"
style={{ color: "white" }}
>
<img src={require('../../../image/icon.png') }></img>
......
......@@ -75,44 +75,7 @@ const styles = theme => ({
right: theme.spacing(2)
}
});
const StyledTableCell = withStyles(theme => ({
head: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white
},
body: {
fontSize: 14
}
}))(TableCell);
const StyledTableRow = withStyles(theme => ({
root: {
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.background.default
}
}
}))(TableRow);
function createData(name, email, address, phonenumber, edit, action) {
return { name, email, address, phonenumber, edit, action };
}
const rows = [
createData(
"Frozen yoghurt",
"email@mmmj",
"ที่อยู่ 120/255 มหาวิทยาลัยุบลราชธานี ",
" 012455887",
<EditIcon />,
<DeleteIcon />
)
];
const useStyles = makeStyles({
table: {
minWidth: 700
}
});
class Work extends Component {
state = {
......@@ -126,7 +89,7 @@ class Work extends Component {
],
data: [
{ name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
{ name: "Benz Piyaphorn", list: "อบไอน้ำ", time: "12:00" },
],
selectedDate : new Date()
......
......@@ -71,22 +71,16 @@ class Databeautician extends Component {
open: false,
columns: [
{ title: "Name", field: "name" },
{ title: "E-mail", field: "surname" },
{ title: "Address", field: "birthYear", type: "numeric" },
{ title: "E-mail", field: "email" },
{ title: "Address", field: "address"},
{
title: "Phone number",
field: "birthCity",
lookup: { 34: "İstanbul", 63: "Şanlıurfa" }
field: "phone"
}
],
data: [
{ name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
{
name: "Zerya Betül",
surname: "Baran",
birthYear: 2017,
birthCity: 34
}
data: [ {name:"ปิยพร อาภรศรี", email:"piyaphorn.ar@gmail.com",address:"7/1 ต.หนองบก",phone:"0647183784"}
],
name: "",
......@@ -384,6 +378,7 @@ class Databeautician extends Component {
})
.then(response => {
console.log("สร้างผู้ใช้สำเร็จ", response);
this.handleClose();
})
......
......@@ -2,38 +2,45 @@ import React, { Component } from "react";
import { withStyles } from "@material-ui/core/styles";
import compose from "recompose/compose";
import { withRouter } from "react-router-dom";
import Input from "@material-ui/core/Input";
import InputLabel from "@material-ui/core/InputLabel";
import InputAdornment from "@material-ui/core/InputAdornment";
import FormControl from "@material-ui/core/FormControl";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";
import {
Input,
InputLabel,
InputAdornment,
FormControl,
TextField,
Grid,
Button,
TextareaAutosize,
Fab,
Paper,
CardMedia,
MenuItem,
Snackbar
} from "@material-ui/core";
import AccountCircle from "@material-ui/icons/AccountCircle";
import AddPhotoAlternateIcon from "@material-ui/icons/AddPhotoAlternate";
import MailOutlineIcon from "@material-ui/icons/MailOutline";
import VpnKeyIcon from "@material-ui/icons/VpnKey";
import Button from "@material-ui/core/Button";
import TextareaAutosize from "@material-ui/core/TextareaAutosize";
import ContactPhoneIcon from "@material-ui/icons/ContactPhone";
import BusinessIcon from "@material-ui/icons/Business";
import Fab from "@material-ui/core/Fab";
import EditIcon from "@material-ui/icons/Edit";
import Paper from "@material-ui/core/Paper";
import CardMedia from "@material-ui/core/CardMedia";
import MenuItem from "@material-ui/core/MenuItem";
import PhotoCameraIcon from "@material-ui/icons/PhotoCamera";
import PostAddIcon from '@material-ui/icons/PostAdd';
import CancelIcon from '@material-ui/icons/Cancel';
import PostAddIcon from "@material-ui/icons/PostAdd";
import CancelIcon from "@material-ui/icons/Cancel";
import axios from "axios";
import MuiAlert from "@material-ui/lab/Alert";
import Avatar from "react-avatar";
const styles = theme => ({
margin: {
margin: theme.spacing(1)
},
root: {
"& .MuiTextField-root": {
margin: theme.spacing(1),
width: 200
marginTop: theme.spacing(2),
width: "100%"
}
}
});
......@@ -71,18 +78,48 @@ const currencies = [
class Datashop extends Component {
state = {
currency: "1"
currency: "1",
name: "",
timeopen: "",
timeclose: "",
tel: "",
address: "",
detail: "",
map: "",
facebook: "",
type: "",
promotion: ""
};
handleChange = event => {
console.log("name : ", event.target.name);
console.log("value : ", event.target.value);
this.setState({
currency: event.target.value
[event.target.name]: event.target.value
});
};
// handleChange = event => {
// this.setState({
// currency: event.target.value
// });
// };
render() {
const { classes } = this.props;
const { currency } = this.state;
const {
currency,
name,
timeopen,
timeclose,
tel,
address,
detail,
map,
facebook,
type,
promotion
} = this.state;
return (
<div className="row center">
<div className="row mt-2 mb-2">
......@@ -110,7 +147,9 @@ class Datashop extends Component {
<div ClassName="row">
<TextField
className={classes.margin}
id="outlined-uncontrolled"
id="name"
name="name"
value={name}
label="ชื่อร้าน"
variant="outlined"
InputProps={{
......@@ -120,11 +159,14 @@ class Datashop extends Component {
</InputAdornment>
)
}}
onChange={this.handleChange}
/>
<TextField
className={classes.margin}
id="outlined-password-input"
id="timeopen"
name="timeopen"
value={timeopen}
label="เวลาเปิดร้าน"
autoComplete="Phone number"
variant="outlined"
......@@ -135,10 +177,13 @@ class Datashop extends Component {
</InputAdornment>
)
}}
onChange={this.handleChange}
/>
<TextField
className={classes.margin}
id="outlined-password-input"
id="timeclose"
name="timeclose"
value={timeclose}
label="เวลาปิดร้าน"
autoComplete="Phone number"
variant="outlined"
......@@ -149,13 +194,16 @@ class Datashop extends Component {
</InputAdornment>
)
}}
onChange={this.handleChange}
/>
</div>
<br />
<div>
<TextField
className={classes.margin}
id="outlined-password-input"
id="tel"
name="tel"
value={tel}
label="เบอร์โทรร้าน"
autoComplete="address"
variant="outlined"
......@@ -166,10 +214,13 @@ class Datashop extends Component {
</InputAdornment>
)
}}
onChange={this.handleChange}
/>
<TextField
className={classes.margin}
id="outlined-password-input"
id="address"
name="address"
value={address}
label="ที่อยู่ร้าน"
autoComplete="Phone number"
variant="outlined"
......@@ -180,10 +231,13 @@ class Datashop extends Component {
</InputAdornment>
)
}}
onChange={this.handleChange}
/>
<TextField
className={classes.margin}
id="outlined-password-input"
id="detail"
name="detail"
value={detail}
label="รายละเอียดร้าน"
autoComplete="address"
variant="outlined"
......@@ -194,13 +248,16 @@ class Datashop extends Component {
</InputAdornment>
)
}}
onChange={this.handleChange}
/>
</div>
<br />
<div>
<TextField
className={classes.margin}
id="outlined-password-input"
id="map"
name="map"
value={map}
label="GoogleMap"
autoComplete="address"
variant="outlined"
......@@ -211,10 +268,13 @@ class Datashop extends Component {
</InputAdornment>
)
}}
onChange={this.handleChange}
/>
<TextField
className={classes.margin}
id="outlined-password-input"
id="facebook"
name="facebook"
value={facebook}
label="Facebook"
autoComplete="address"
variant="outlined"
......@@ -225,13 +285,15 @@ class Datashop extends Component {
</InputAdornment>
)
}}
onChange={this.handleChange}
/>
<TextField
className={classes.margin}
id="outlined-password-input"
id="type"
name="type"
value={type}
select
label="ประเภทของร้าน"
value={currency}
onChange={this.handleChange}
SelectProps={{
native: true
......@@ -244,6 +306,7 @@ class Datashop extends Component {
</InputAdornment>
)
}}
onChange={this.handleChange}
>
{currencies.map(option => (
<option key={option.value} value={option.value}>
......@@ -260,7 +323,33 @@ class Datashop extends Component {
ยกเลิก
</Button>{" "}
&nbsp; &nbsp; &nbsp;
<Button variant="contained" color="primary">
<Button
variant="contained"
color="primary"
onClick={() => {
axios
.post("http://localhost:9000/api/shop/add", {
name: name,
timeopen: timeopen,
timeclose: timeclose,
tel: tel,
address: address,
detail: detail,
map: map,
facebook: facebook,
type: type
})
.then(response => {
console.log("สร้างผู้ใช้สำเร็จ", response);
alert("เพิ่มข้อมูลสำเร็จ");
// window.location.href = "/";
})
.catch(error => {
console.log(error);
});
// alert("ลงทะเบียนสำเร็จ");
}}
>
ตกลง
</Button>
&nbsp; &nbsp; &nbsp;
......@@ -279,23 +368,45 @@ class Datashop extends Component {
<TextField
className={classes.margin}
id="promotion"
name="promotion"
value={promotion}
label="ข้อมูลโปรโมชั่น"
autoComplete="promotion"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<PostAddIcon/>
<PostAddIcon />
</InputAdornment>
)
}}
onChange={this.handleChange}
/>
&nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp;
<Button variant="contained" color="secondary">
ยกเลิก
</Button>{" "}
&nbsp; &nbsp; &nbsp;
<Button variant="contained" color="primary">
<Button
variant="contained"
color="primary"
onClick={() => {
axios
.post("http://localhost:9000/api/promotion/add", {
detail: promotion,
shop: "5"
})
.then(response => {
console.log("สร้างผู้ใช้สำเร็จ", response);
alert("เพิ่มข้อมูลสำเร็จ");
// window.location.href = "/";
})
.catch(error => {
console.log(error);
});
// alert("ลงทะเบียนสำเร็จ");
}}
>
ตกลง
</Button>
&nbsp; &nbsp; &nbsp;
......
......@@ -102,7 +102,7 @@ class ResponsiveDrawer extends Component {
color="inherit"
aria-label="menu"
Link
href="/"
href="/DataShopPage"
style={{ color: "white" }}
>
<img src={require('../../../image/icon.png') }></img>
......
......@@ -41,7 +41,7 @@ import VpnKeyIcon from "@material-ui/icons/VpnKey";
import TextareaAutosize from "@material-ui/core/TextareaAutosize";
import ContactPhoneIcon from "@material-ui/icons/ContactPhone";
import BusinessIcon from "@material-ui/icons/Business";
import axios from "axios";
import MaterialTable from "material-table";
const styles = theme => ({
......@@ -67,29 +67,6 @@ const styles = theme => ({
right: theme.spacing(2)
}
});
const StyledTableCell = withStyles(theme => ({
head: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white
},
body: {
fontSize: 14
}
}))(TableCell);
const StyledTableRow = withStyles(theme => ({
root: {
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.background.default
}
}
}))(TableRow);
const useStyles = makeStyles({
table: {
minWidth: 700
}
});
class Databeautician extends Component {
state = {
......@@ -98,13 +75,40 @@ class Databeautician extends Component {
{ title: "Name", field: "name" },
{ title: "Price", field: "price" },
{ title: "Time", field: "time" }
],
data: [
{ name: "Mehmet", price:"200", time: "Baran"},
]
name: "สปา",
price: "200",
time: "20",
data: [{ name: "อบไอน้ำ", price: "200", time: "20" }]
};
componentDidMount = () => {
axios
.get("http://localhost:9000/api/list/getListshop/5")
.then(function(response) {
// handle success
// console.log("response : ", response.data);
// const name = response.data.name;
// const email = response.data.email;
const { name, price, time } = response.data;
console.log("name : ", name);
console.log("price : ", price);
this.setState({
// data: [{ name: name, price: price, time: time }]
name: name,
price: price,
time: time
});
})
.catch(function(error) {
// handle error
console.log(error);
});
console.log("here");
};
handleClickOpen = () => {
this.setState({
open: true
......@@ -121,8 +125,9 @@ class Databeautician extends Component {
this.setState(prevState => {
const data = [...prevState.data];
const newData = {
name: "Mehmet",
time: "Baran"
name: "",
price: "",
time: ""
};
data.push(newData);
return { ...prevState, data: data, open: false };
......@@ -141,9 +146,10 @@ class Databeautician extends Component {
render() {
const { classes } = this.props;
const { open, columns, data } = this.state;
const { open, columns, data, name, price, time } = this.state;
return (
<div>
<MaterialTable
title="รายการ"
columns={columns}
......
import React, { Component } from "react";
import { withStyles, makeStyles } from "@material-ui/core/styles";
import { withStyles } from "@material-ui/core/styles";
import {
Dialog,
Button,
......@@ -26,7 +26,7 @@ import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import DoneIcon from '@material-ui/icons/Done';
import DoneIcon from "@material-ui/icons/Done";
import Input from "@material-ui/core/Input";
import InputLabel from "@material-ui/core/InputLabel";
import InputAdornment from "@material-ui/core/InputAdornment";
......@@ -42,13 +42,13 @@ import ContactPhoneIcon from "@material-ui/icons/ContactPhone";
import BusinessIcon from "@material-ui/icons/Business";
import { DatePicker } from "@material-ui/pickers";
import { injectIntl, FormattedRelativeTime } from "react-intl";
import CheckIcon from "@material-ui/icons/Check";
import {
MuiPickersUtilsProvider,
KeyboardTimePicker,
KeyboardDatePicker,
} from '@material-ui/pickers';
MuiPickersUtilsProvider,
KeyboardTimePicker,
KeyboardDatePicker
} from "@material-ui/pickers";
import MaterialTable from "material-table";
const styles = theme => ({
......@@ -77,20 +77,23 @@ const styles = theme => ({
class Managequeue extends Component {
state = {
columns: [
{ title: "ชื่อคนจอง", field: "name" },
{ title: "รายการจอง", field: "list" },
{ title: "เวลาที่จอง", field: "time" },
{ title: "ช่างที่จอง", field: "booking" }
],
data: [
{ name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
{
name: "Benz Piyaphorn",
list: "อบไอน้ำ",
time: "12:00",
booking: "ปิยพร อาภรศรี"
}
],
selectedDate : new Date()
selectedDate: new Date()
};
handleChangeDate = newDate => {
console.log("newDate: ", newDate);
......@@ -98,7 +101,7 @@ class Managequeue extends Component {
date: newDate
});
};
handleCloseAndSave = () => {
//Save to db
......@@ -154,17 +157,18 @@ class Managequeue extends Component {
selection: false
}}
editable={{
// onRowAdd: newData =>
// new Promise(resolve => {
// new Promise(resolve => {
// setTimeout(() => {
// resolve();
// this.setState(prevState => {
// const data = [...prevState.data];
// data.push(newData);
// return { ...prevState, data };
// });
// }, 600);
// }),
// resolve();
// this.setState(prevState => {
// const data = [...prevState.data];
// data.push(newData);
// return { ...prevState, data };
// });
// }, 600);
// }),
onRowUpdate: (newData, oldData) =>
new Promise(resolve => {
setTimeout(() => {
......@@ -178,19 +182,19 @@ class Managequeue extends Component {
}
}, 600);
}),
// onRowDelete: oldData =>
// new Promise(resolve => {
// setTimeout(() => {
// resolve();
// this.setState(prevState => {
// const data = [...prevState.data];
// data.splice(data.indexOf(oldData), 1);
// return { ...prevState, data };
// });
// }, 600);
// })
}}
// onRowDelete: oldData =>
// new Promise(resolve => {
// setTimeout(() => {
// resolve();
// this.setState(prevState => {
// const data = [...prevState.data];
// data.splice(data.indexOf(oldData), 1);
// return { ...prevState, data };
// });
// }, 600);
// })
}}
/>
</div>
</div>
......
......@@ -10,7 +10,9 @@ import {
Paper,
InputBase,
Divider,
IconButton
IconButton,
InputAdornment,
TextField
} from "@material-ui/core";
import AppBra from "../../components/AppBra";
import { withRouter } from "react-router-dom";
......@@ -20,19 +22,29 @@ import { injectIntl, FormattedRelativeTime } from "react-intl";
import { th } from "date-fns/locale";
import { format } from "date-fns";
import MenuIcon from "@material-ui/icons/Menu";
import TextField from "@material-ui/core/TextField";
import SearchIcon from "@material-ui/icons/Search";
import DirectionsIcon from "@material-ui/icons/Directions";
import ListIcon from "@material-ui/icons/List";
import AssignmentIndIcon from "@material-ui/icons/AssignmentInd";
import Rating from "@material-ui/lab/Rating";
const styles = theme => ({
root: {
padding: "2px 4px",
// padding: "2px 4px",
display: "flex",
alignItems: "center",
width: 400
width: 400,
"& .MuiTextField-root": {
margin: theme.spacing(1),
marginTop: theme.spacing(2),
width: "100%"
}
},
margin: {
margin: theme.spacing(1)
},
input: {
marginLeft: theme.spacing(1),
flex: 1
......@@ -51,12 +63,42 @@ const styles = theme => ({
justifyContent: "center"
}
});
const currencies = [
{
value: "1",
label: " กรุณาเลือกประเภท "
},
{
value: "2",
label: "สปาและนวด"
},
{
value: "3",
label: "ผิวหน้า"
},
{
value: "4",
label: "ขนตาและคิ้ว"
},
{
value: "5",
label: "ทำเล็บ"
},
{
value: "6",
label: "ชาลอน"
},
{
value: "7",
label: "แต่งหน้าทำผม"
}
];
class HomePage extends Component {
state = {
tab: 0,
date: new Date(),
open: false,
list: false,
rating: 0
};
handleClickOpen = () => {
......@@ -69,14 +111,14 @@ class HomePage extends Component {
open: false
});
};
handleClickOpen2 = () => {
ClickOpen = () => {
this.setState({
open: true
list: true
});
};
handleClose2 = () => {
Close = () => {
this.setState({
open: false
list: false
});
};
......@@ -135,12 +177,12 @@ class HomePage extends Component {
const { classes } = this.props;
// const tab = this.state.tab;
// หรือ
const { tab, date, open, value, rating } = this.state;
const { tab, date, open, list, value, rating } = this.state;
return (
<div>
<AppBra tab={tab} handleChangeTab={this.handleChangeTab} />
<br />
<div className="row ">
<div className="row mt-5 ">
<div className="row mt-2 mb-2 ">
<div className="row ">
<center>
......@@ -171,45 +213,42 @@ class HomePage extends Component {
</center>
</div>
</div>
<div className="row mt-2 mb-2 center">
<div className="col s12 m16 l16">
<h3>ชื่อ</h3>
</div>
</div>
<div className="row mt-2 mb-2 ">
<div className="col s12 m6 l8">
<div className="row center">
<div className="row mt-5 mb-2 ">
<div className="col s12 m6 l6">
<div className="row mt-3 mb-2 center">
<img
src="https://www.smeleader.com/wp-content/uploads/2018/05/%E0%B9%81%E0%B8%9F%E0%B8%A3%E0%B8%99%E0%B9%84%E0%B8%8A%E0%B8%AA%E0%B9%8C%E0%B8%A3%E0%B9%89%E0%B8%B2%E0%B8%99%E0%B9%80%E0%B8%AA%E0%B8%A3%E0%B8%B4%E0%B8%A1%E0%B8%AA%E0%B8%A7%E0%B8%A2-%E0%B8%A3%E0%B8%A7%E0%B8%A1%E0%B9%81%E0%B8%9A%E0%B8%A3%E0%B8%99%E0%B8%94%E0%B9%8C%E0%B8%A3%E0%B9%89%E0%B8%B2%E0%B8%99%E0%B8%8B%E0%B8%B2%E0%B8%A5%E0%B8%AD%E0%B8%99-%E0%B8%AA%E0%B8%B2%E0%B8%99%E0%B8%9D%E0%B8%B1%E0%B8%99%E0%B8%98%E0%B8%B8%E0%B8%A3%E0%B8%81%E0%B8%B4%E0%B8%88%E0%B8%97%E0%B8%A3%E0%B8%87%E0%B8%9C%E0%B8%A1.jpg"
width="70%"
width="60%"
onClick={() => {
this.props.history.push("/ShopPage");
}}
/>
</div>
</div>
<div className="col s12 m6 l4">
<div className="row">
<h4>
ที่อยู่ : 85 Sathonlamak Rd, Mueang Si Khai, Warin Chamrap
District, Ubon Ratchathani 34190
</h4>
<div className="row mt-2 mb-2">
<h3>น้องใหม่บิวตี้ NongMai Beauty</h3>
</div>
<div className="row">
<h4>โปรโมชั่น : จองคิวผ่าน B Beauty ลด 5 %</h4>
<div className="row mt-2 mb-2">
ที่อยู่ : 10 หมู่ 1 บ้านโพนเมือง ตำบลโพนเมือง อำเภอเหล่าเสือโก้ก
จังหวัดอุบลราชธานี 34000
</div>
<div className="row">
<Rating
name="simple-controlled"
value={rating}
size="large"
onChange={(event, newValue) => {
//change rating
this.setState({
rating: newValue
});
}}
/>
<div className="row mt-3 mb-2">
โปรโมชั่น : จองคิวผ่าน B Beauty ลด 5 %
</div>
<div className="row mt-3 mb-2">
<div className={classes.root}>
<Rating
name="size-large"
defaultValue={3.5}
size="large"
readOnly
/>
</div>
</div>
<div className="row center">
......@@ -245,19 +284,93 @@ class HomePage extends Component {
/>
</DialogContent>
<DialogActions>
<Button variant="outlined" onClick={this.handleClose} color="primary">
<Button
variant="outlined"
onClick={this.handleClose}
color="primary"
>
ยกเลิก
</Button>
<Button
variant="outlined"
color="primary"
onClick={this.handleClickOpen}
onClick={this.ClickOpen}
>
ถัดไป
</Button>
</DialogActions>
</Dialog>
<Dialog
open={list}
onClose={this.Close}
aria-labelledby="max-width-dialog-title"
>
<DialogTitle id="max-width-dialog-title">
กรุณาเลือกข้อมูลการจอง
</DialogTitle>
<DialogContent>
<DialogContentText>
<TextField
className={classes.margin}
id="list"
name="list"
select
label="เลือกรายการ"
onChange={this.handleChange}
SelectProps={{
native: true
}}
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<ListIcon />
</InputAdornment>
)
}}
>
{/* {currencies.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))} */}
</TextField>
<TextField
className={classes.margin}
id="list"
name="list"
select
label="เลือกช่าง"
onChange={this.handleChange}
SelectProps={{
native: true
}}
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AssignmentIndIcon />
</InputAdornment>
)
}}
>
{/* {currencies.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))} */}
</TextField>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.Close} color="primary">
Cancel
</Button>
<Button onClick={this.Close} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
</div>
</div>
......
......@@ -8,12 +8,13 @@ import InputAdornment from "@material-ui/core/InputAdornment";
import FormControl from "@material-ui/core/FormControl";
import TextField from "@material-ui/core/TextField";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import AccountCircle from "@material-ui/icons/AccountCircle";
import AddPhotoAlternateIcon from "@material-ui/icons/AddPhotoAlternate";
import MailOutlineIcon from "@material-ui/icons/MailOutline";
import VpnKeyIcon from "@material-ui/icons/VpnKey";
import Button from "@material-ui/core/Button";
import Link from '@material-ui/core/Link';
import Link from "@material-ui/core/Link";
import axios from "axios";
const styles = theme => ({
......@@ -27,7 +28,8 @@ const styles = theme => ({
class Login extends Component {
state = {
email: "",
password: ""
password: "",
role: ""
};
handleChange = event => {
......@@ -41,12 +43,16 @@ class Login extends Component {
render() {
const { classes } = this.props;
const { email, password } = this.state;
const { email, password, role } = this.state;
return (
<div>
<div className="row center">
<h2>เข้าสู่ระบบ</h2>
<br />
<br />
<div className="row mt-5 ">
<h2>เข้าสู่ระบบ</h2>
</div>
<div>
<TextField
className={classes.margin}
......@@ -88,15 +94,53 @@ class Login extends Component {
</div>
<br></br>
<div>
<Button variant="contained" color="primary" disableElevation>
<Button
variant="contained"
color="primary"
disableElevation
onClick={() => {
axios
.post("http://localhost:9000/api/auth/login", {
email: email,
password: password
})
.then(async response => {
const token = response.data.token;
localStorage.setItem("token", token);
const user = await axios.get(
"http://localhost:9000/api/auth/currentUser",
{ headers: { token: token } }
);
if (user) {
if (user.data.role === 0) {
this.props.history.push("/");
} else if (user.data.role === 1) {
this.props.history.push("/DataShopPage");
} else {
this.props.history.push("/BeauticianShopPage");
}
}
})
.catch(error => {
console.log(error);
});
// alert("ลงทะเบียนสำเร็จ");
}}
>
เข้าสู่ระบบ
</Button>
</div>
<br/><br/>
<br />
<br />
<div className="row center">
<h4>
คุณลืมรหัสผ่าน ? <Link href="/">Forgot Password</Link>
</h4>
</div>
<div className="row center">
<Link href="/RegisterPage" >
ยังไม่ได้ลงทะเบียน
</Link>
<h4>
คุณยังไม่ลงทะเบียน ? <Link href="/RegisterPage">ลงทะเบียน</Link>
</h4>
</div>
</div>
</div>
......
......@@ -59,8 +59,11 @@ class RegisterPage extends Component {
role
} = this.state;
return (
<div className="row center">
<h2>ลงทะเบียน</h2>
<div className="row center ">
<div className="row mt-5 ">
<h2>ลงทะเบียน</h2>
</div>
<br></br>
<div>
<TextField
......@@ -214,11 +217,11 @@ class RegisterPage extends Component {
image: image,
address: address,
tel: tel,
role: '0'
role: "0"
})
.then(response => {
console.log("สร้างผู้ใช้สำเร็จ", response);
window.location.href = "/";
this.props.history.push("/");
})
.catch(error => {
console.log(error);
......
......@@ -218,7 +218,7 @@ class RegisterShopPage extends Component {
})
.then(response => {
console.log("สร้างผู้ใช้สำเร็จ", response);
window.location.href = "/";
this.props.history.push('/')
})
.catch(error => {
console.log(error);
......
......@@ -3,20 +3,49 @@ import { withStyles } from "@material-ui/core/styles";
import compose from "recompose/compose";
import { withRouter } from "react-router-dom";
import AppBar from "../../components/AppBra";
import Button from "@material-ui/core/Button";
import Card from "@material-ui/core/Card";
import CardActions from "@material-ui/core/CardActions";
import CardContent from "@material-ui/core/CardContent";
import Typography from "@material-ui/core/Typography";
import {
Button,
CardHeader,
Avatar,
IconButton,
CardMedia,
CardActions,
CardContent,
Typography,
Card,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
InputAdornment,
TextField,
InputBase,
} from "@material-ui/core";
import MoreVertIcon from "@material-ui/icons/MoreVert";
import { DatePicker } from "@material-ui/pickers";
import { injectIntl, FormattedRelativeTime } from "react-intl";
import { th } from "date-fns/locale";
import { format } from "date-fns";
import Rating from "@material-ui/lab/Rating";
import ListIcon from "@material-ui/icons/List";
import AssignmentIndIcon from "@material-ui/icons/AssignmentInd";
import axios from "axios";
// const axios = require('axios');
const styles = theme => ({
root: {
minWidth: 275
minWidth: 275,
height: 150,
flexGrow: 1,
transform: "translateZ(0)",
// The position fixed scoping doesn't work in IE 11.
// Disable this demo to preserve the others.
"@media all and (-ms-high-contrast: none)": {
display: "none"
}
},
bullet: {
display: "inline-block",
margin: "0 2px",
......@@ -27,12 +56,69 @@ const styles = theme => ({
},
pos: {
marginBottom: 12
},
modal: {
display: "flex",
padding: theme.spacing(1),
alignItems: "center",
justifyContent: "center"
},
fab: {
position: "absolute",
bottom: theme.spacing(2),
right: theme.spacing(2)
}
});
class ShopPage extends Component {
state = {
name: "xxx",
email: "xyz.ggg.com"
email: "xyz.ggg.com",
reviwe: false,
list: false,
date: new Date(),
open: false,
data: false
};
handleClickOpen = () => {
this.setState({
reviwe: true
});
};
handleClose = () => {
this.setState({
reviwe: false
});
};
handleClickOpenQ = () => {
this.setState({
open: true
});
};
handleCloseQ = () => {
this.setState({
open: false
});
};
ClickOpen = () => {
this.setState({
list: true
});
};
Close = () => {
this.setState({
list: false
});
};
ClickOpendata = () => {
this.setState({
data: true
});
};
Closedata = () => {
this.setState({
data: false
});
};
componentDidMount = () => {
......@@ -50,9 +136,8 @@ class ShopPage extends Component {
this.setState({
name: name,
email:email
email: email
});
})
.catch(function(error) {
// handle error
......@@ -65,49 +150,177 @@ class ShopPage extends Component {
render() {
const { classes } = this.props;
const bull = <span className={classes.bullet}></span>;
const { name, email } = this.state;
const { name, email, reviwe, list, date, open ,data} = this.state;
return (
<div>
<AppBar />
<div className="row ">
<div className="row mt-2 mb-2">
<div className="col s12 m6 l6">
<center>
<h3>ชื่อ</h3>
</center>
<div className="col s12 m6 l1"></div>
<div className="col s12 m6 l11">
<h3>น้องใหม่บิวตี้ NongMai Beauty</h3>
10 หมู่ 1 บ้านโพนเมือง ตำบลโพนเมือง อำเภอเหล่าเสือโก้ก
จังหวัดอุบลราชธานี 34000
</div>
</div>
<div className="row mt-2 mb-2">
<div className="row mt-4 mb-2">
<div className="col s12 m6 l8">
<div>
<div className="row center">
<img
src="https://www.smeleader.com/wp-content/uploads/2018/05/%E0%B9%81%E0%B8%9F%E0%B8%A3%E0%B8%99%E0%B9%84%E0%B8%8A%E0%B8%AA%E0%B9%8C%E0%B8%A3%E0%B9%89%E0%B8%B2%E0%B8%99%E0%B9%80%E0%B8%AA%E0%B8%A3%E0%B8%B4%E0%B8%A1%E0%B8%AA%E0%B8%A7%E0%B8%A2-%E0%B8%A3%E0%B8%A7%E0%B8%A1%E0%B9%81%E0%B8%9A%E0%B8%A3%E0%B8%99%E0%B8%94%E0%B9%8C%E0%B8%A3%E0%B9%89%E0%B8%B2%E0%B8%99%E0%B8%8B%E0%B8%B2%E0%B8%A5%E0%B8%AD%E0%B8%99-%E0%B8%AA%E0%B8%B2%E0%B8%99%E0%B8%9D%E0%B8%B1%E0%B8%99%E0%B8%98%E0%B8%B8%E0%B8%A3%E0%B8%81%E0%B8%B4%E0%B8%88%E0%B8%97%E0%B8%A3%E0%B8%87%E0%B8%9C%E0%B8%A1.jpg"
width="90%"
></img>
</div>
<div className="row center">
<Button variant="contained" color="secondary">
<Button
variant="contained"
color="secondary"
onClick={this.ClickOpen}
>
รายการ
</Button>{" "}
<Dialog
open={list}
onClose={this.Close}
aria-labelledby="max-width-dialog-title"
>
<DialogTitle id="max-width-dialog-title">รายการ</DialogTitle>
<DialogContent>
<DialogContentText>
You can set my maximum width and whether to adapt or not.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.Close} color="primary">
Close
</Button>
</DialogActions>
</Dialog>
&nbsp; &nbsp; &nbsp;
<Button variant="contained" color="primary">
<Button
variant="contained"
color="primary"
onClick={this.handleClickOpenQ}
>
จองคิว
</Button>
<Dialog
open={open}
onClose={this.handleCloseQ}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"จองคิว"}</DialogTitle>
<DialogContent>
<h6>
วันที่ที่เลือกคือ{" "}
{format(new Date(date), "dd MMMM yyyy", {
locale: th
})}
</h6>
<DatePicker
autoOk
orientation="static"
variant="static"
openTo="date"
minDate={new Date()}
value={date}
onChange={this.handleChangeDate}
/>
</DialogContent>
<DialogActions>
<Button
variant="outlined"
onClick={this.handleCloseQ}
color="primary"
>
ยกเลิก
</Button>
<Button
variant="outlined"
color="primary"
onClick={this.ClickOpendata}
>
ถัดไป
</Button>
</DialogActions>
</Dialog>
<Dialog
open={data}
onClose={this.Closedata}
aria-labelledby="max-width-dialog-title"
>
<DialogTitle id="max-width-dialog-title">
กรุณาเลือกข้อมูลการจอง
</DialogTitle>
<DialogContent>
<DialogContentText>
<TextField
className={classes.margin}
id="list"
name="list"
select
label="เลือกรายการ"
onChange={this.handleChange}
SelectProps={{
native: true
}}
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<ListIcon />
</InputAdornment>
)
}}
>
{/* {currencies.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))} */}
</TextField>&nbsp;&nbsp;
<TextField
className={classes.margin}
id="list"
name="list"
select
label="เลือกช่าง"
onChange={this.handleChange}
SelectProps={{
native: true
}}
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AssignmentIndIcon />
</InputAdornment>
)
}}
>
{/* {currencies.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))} */}
</TextField>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.Closedata} color="primary">
Cancel
</Button>
<Button onClick={this.Closedata} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
</div>
<div className="col s12 m6 l4">
<div className="row">
<h4>
{" "}
ที่อยู่ : 85 Sathonlamak Rd, Mueang Si Khai, Warin Chamrap
District, Ubon Ratchathani 34190
</h4>
</div>
<div className="row">
<center>
<img
......@@ -117,7 +330,14 @@ class ShopPage extends Component {
</center>
</div>
<div className="row">
<h4>เบอร์โทร :08xxxxxx</h4>
<h4>
ที่อยู่ : 10 หมู่ 1 บ้านโพนเมือง ตำบลโพนเมือง
อำเภอเหล่าเสือโก้ก จังหวัดอุบลราชธานี 34000
</h4>
</div>
<div className="row">
<h4>เบอร์โทร : 082-547-8955</h4>
<h4>เวลาเปิด : 08:00 .</h4>
<h4>เวลาปิด : 18:00 .</h4>
</div>
......@@ -127,15 +347,15 @@ class ShopPage extends Component {
<div className="row mt-2 mb-2">
<div className="col s12 m6 l7">
<div className="row center">
<Card className={classes.root} variant="outlined">
<h4>ผลงานช่าง นาย</h4>
<h4>ผลงานช่าง ปิยพร อาภรศรี</h4>
<div className="col s12 m6 l4">
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcSAaEt_unwBuDLyqCP_bW3PBawVHkjZrNq-F3u7mAKtSjmBTrHE"></img>
</div>
<div className="col s12 m6 l4">
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcSAaEt_unwBuDLyqCP_bW3PBawVHkjZrNq-F3u7mAKtSjmBTrHE"></img>
</div>
</Card>
</div>
</div>
<div className="col s12 m6 l1"></div>
......@@ -151,56 +371,94 @@ class ShopPage extends Component {
color="textSecondary"
gutterBottom
>
โปรโมชั่น
</Typography>
<Typography variant="h5" component="h2">
be{bull}nev{bull}o{bull}lent
</Typography>
<Typography className={classes.pos} color="textSecondary">
adjective
<h3>โปรโมชั่น</h3>
</Typography>
<Typography variant="body2" component="p">
well meaning and kindly.
<br />
{'"a benevolent smile"'}
<h4> จองคิวผ่าน B Beauty ลด 5 %</h4>
{'"12/02/2020 18:00"'}
</Typography>
</CardContent>
<CardActions>
{/* <CardActions>
<Button size="small">Learn More</Button>
</CardActions>
</CardActions> */}
</Card>
</div>
<div className="row">
<Card className={classes.root} variant="outlined">
<CardHeader
avatar={
<Avatar aria-label="recipe" className={classes.avatar}>
R
</Avatar>
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title="Shrimp and Chorizo Paella"
subheader="September 14, 2016"
/>
<div className="row center">
<div className={classes.root}>
<Rating
name="size-large"
defaultValue={3.5}
size="small"
readOnly
/>
</div>
</div>
<CardMedia
className={classes.media}
image="/static/images/cards/paella.jpg"
title="Paella dish"
/>
<CardContent>
<Typography
className={classes.title}
variant="body2"
color="textSecondary"
gutterBottom
component="p"
>
รีวิว
</Typography>
<Typography variant="h5" component="h2">
be{bull}nev{bull}o{bull}lent
</Typography>
<Typography className={classes.pos} color="textSecondary">
adjective
</Typography>
<Typography variant="body2" component="p">
well meaning and kindly.
<br />
{'"a benevolent smile"'}
This impressive paella is a perfect party dish and a fun
meal to cook together with your guests. Add 1 cup of
frozen peas along with the mussels, if you like.
</Typography>
</CardContent>
<CardActions>
<Button size="small">Learn More</Button>
</CardActions>
</Card>
</div>
<div className="row right">
<Button variant="contained" color="primary">
<Button
variant="contained"
color="primary"
onClick={this.handleClickOpen}
>
เขียนรีวิว
</Button>
<Dialog
open={reviwe}
onClose={this.handleClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Subscribe</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email
address here. We will send updates occasionally.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color="primary">
Cancel
</Button>
<Button onClick={this.handleClose} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
</div>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment