├── .gitignore ├── Procfile ├── README.md ├── app.js ├── bin └── www ├── controllers ├── adminController.js └── apiController.js ├── middlewares ├── auth.js └── multer.js ├── models ├── Activity.js ├── Bank.js ├── Booking.js ├── Category.js ├── Feature.js ├── Image.js ├── Item.js ├── Member.js └── Users.js ├── package-lock.json ├── package.json ├── public ├── images │ ├── activity-1.png │ ├── activity-2.png │ ├── activity-3.png │ ├── activity-4.png │ ├── buktibayar.jpeg │ ├── feature-1.png │ ├── feature-2.png │ ├── feature-3.png │ ├── feature-4.png │ ├── feature-5.png │ ├── feature-6.png │ ├── feature-7.png │ ├── feature-8.png │ ├── image-category-1-min.jpg │ ├── image-category-10-min.jpg │ ├── image-category-11-min.jpg │ ├── image-category-12-min.jpg │ ├── image-category-13-min.jpg │ ├── image-category-14-min.jpg │ ├── image-category-15-min.jpg │ ├── image-category-16-min.jpg │ ├── image-category-17-min.jpg │ ├── image-category-18-min.jpg │ ├── image-category-2-min.jpg │ ├── image-category-3-min.jpg │ ├── image-category-4-min.jpg │ ├── image-category-5-min.jpg │ ├── image-category-6-min.jpg │ ├── image-category-7-min.jpg │ ├── image-category-8-min.jpg │ ├── image-category-9-min.jpg │ ├── image-mostpicked-1-min.jpg │ ├── image-mostpicked-10-min.jpg │ ├── image-mostpicked-11-min.jpg │ ├── image-mostpicked-12-min.jpg │ ├── image-mostpicked-13-min.jpg │ ├── image-mostpicked-14-min.jpg │ ├── image-mostpicked-15-min.jpg │ ├── image-mostpicked-2-min.jpg │ ├── image-mostpicked-3-min.jpg │ ├── image-mostpicked-4-min.jpg │ ├── image-mostpicked-5-min.jpg │ ├── image-mostpicked-6-min.jpg │ ├── image-mostpicked-7-min.jpg │ ├── image-mostpicked-8-min.jpg │ ├── image-mostpicked-9-min.jpg │ ├── images seeder.zip │ ├── img-featured-1-min.jpg │ ├── img-featured-2-min.jpg │ ├── img-featured-3-min.jpg │ ├── item-1.png │ ├── item-2.png │ ├── item-3.png │ ├── item-4.png │ ├── item-a-1.png │ ├── item-a-2.png │ ├── item-a-3.png │ ├── item-a-4.png │ ├── item-h-1.png │ ├── item-h-2.png │ ├── item-h-3.png │ ├── logo bca.png │ ├── logo mandiri.png │ ├── testimonial1.jpg │ └── testimonial2.jpg └── stylesheets │ └── style.css ├── routes ├── admin.js ├── api.js ├── index.js └── users.js ├── seed.js ├── test ├── buktibayar.jpeg └── index.js └── views ├── admin ├── bank │ ├── add_modal.ejs │ ├── edit_modal.ejs │ ├── table_bank.ejs │ └── view_bank.ejs ├── booking │ ├── show_detail_booking.ejs │ ├── show_table_detail_booking.ejs │ ├── table_booking.ejs │ └── view_booking.ejs ├── category │ ├── add_modal.ejs │ ├── edit_modal.ejs │ ├── table_category.ejs │ └── view_category.ejs ├── dashboard │ └── view_dashboard.ejs └── item │ ├── add_item.ejs │ ├── detail_item │ ├── show_activity.ejs │ ├── show_feature.ejs │ ├── show_modal_activity.ejs │ ├── show_modal_feature.ejs │ └── view_detail_item.ejs │ ├── edit_item.ejs │ ├── show_image_item.ejs │ ├── show_table_item.ejs │ └── view_item.ejs ├── error.ejs ├── index.ejs └── partials ├── footer.ejs ├── header.ejs ├── js.ejs ├── message.ejs ├── navbar.ejs └── sidebar.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node ./bin/www -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Kelas Online Premium

2 | 3 |

Membuat Website Mencari Kosan/Hotel/Apartment dengan MERN Stack

4 | 5 | MERN Stack Development adalah salah satu techstack yang banyak digunakan oleh web developer saat ini. Mengandalkan ReactJS dalam membuat website lebih interactive dan easy to maintain. 6 | 7 | Kita juga akan menggunakan NodeJS dan ExpressJS untuk handle bagian back-end terutama dalam pembuatan suatu API. 8 | 9 | Di kelas ini juga kita akan belajar UI UX Design agar website lebih mudah digunakan oleh pengguna (sangat bagus jika kamu punya bisnis). 10 | 11 | Pada kelas ini kita akan belajar mengenai: 12 | 1. UI UX design menggunakan Figma 13 | 2. Design handoff yang dilakukan designer kepada developer 14 | 3. Convert design ke Bootstrap dan React JS 15 | 4. Membuat Back-End dengan Node JS dan Express JS 16 | 5. Upload ke vps (heroku) 17 | 18 | # Mentor 19 | 20 | | | | | 21 | | ------------- | -------------------------- | -------------------------------------------- | 22 | | Angga Risky | Product Designer | Mengajar Design (UIUX) | 23 | | Elfin Sanjaya | Expert Backend Developer | Mengajar NodeJS & ExpressJS | 24 | | Yein Narayana | Expert Front End Developer | Mengajar Bootstrap & React JS & Integrasi AP | 25 | 26 | 27 | Tools (semua gratis dan siapa saja bisa belajar): 28 | 1. Windows 10 / Linux / Mac OS 29 | 2. Figma (https://figma.com) 30 | 3. Whimsical (https://whimsical.com/) 31 | 4. FlowMapp (https://flowmapp.com/) 32 | 5. InVision (https://www.invisionapp.com/) 33 | 6. React JS (https://reactjs.org/) 34 | 7. Node JS (https://nodejs.org/en/) 35 | 8. Express JS (https://expressjs.com/) 36 | 9. Visual Studio Code (https://code.visualstudio.com/) 37 | 10. Niat, Usaha dan Kerja Keras. 38 | 11. Mongodb 39 | 40 | 41 | Benefit Unggulan 42 | 1. Materi Desain UI UX 43 | 2. Materi Web Front-End Apps Development (Bootstrap + React JS) 44 | 3. Materi Web Back End Apps Development (Node JS + Express JS + API) 45 | 3. File Master Projek Design 46 | 4. File Master Projek Web Apps 47 | 5. Sertifikat Kelulusan 48 | 6. Konsultasi Gratis Seumur Hidup (Group Private Telegram) 49 | 7. Video kualitas tinggi (1080 HD) 50 | 8. Akses Kelas Seumur Hidup 51 | 9. Audio lebih nyaman untuk didengarkan 52 | 10. Masih banyak lagi, lihat sendiri saja nanti 53 | 54 | # Daftar Materi 55 | 56 | ## UX Design (dibawakan oleh Angga) 57 | * Memahami Brief (Ideation) VIDEO SELESAI DIBUAT 58 | * Membuat Customer Persona VIDEO SELESAI DIBUAT 59 | * Membuat Information Architecture UX VIDEO SELESAI DIBUAT 60 | * Membuat User-Flow VIDEO SELESAI DIBUAT 61 | * Membuat Wireframe 62 | * Landing Page VIDEO SELESAI DIBUAT 63 | * banner VIDEO SELESAI DIBUAT 64 | * navigation header VIDEO SELESAI DIBUAT 65 | * items featured VIDEO SELESAI DIBUAT 66 | * testimonial VIDEO SELESAI DIBUAT 67 | * features VIDEO SELESAI DIBUAT 68 | *footer VIDEO SELESAI DIBUAT 69 | * Details Page VIDEO SELESAI DIBUAT 70 | * information kosan/hotel VIDEO SELESAI DIBUAT 71 | * breadcrumbs (ux sitemap) VIDEO SELESAI DIBUAT 72 | * booking now VIDEO SELESAI DIBUAT 73 | * features kosan VIDEO SELESAI DIBUAT 74 | * testimonial VIDEO SELESAI DIBUAT 75 | * footer VIDEO SELESAI DIBUAT 76 | * header VIDEO SELESAI DIBUAT 77 | * Checkout Page VIDEO SELESAI DIBUAT 78 | * form request book VIDEO SELESAI DIBUAT 79 | * information orders VIDEO SELESAI DIBUAT 80 | 81 | ## Visual Design (dibawakan oleh Angga) 82 | * Membuat MoodBoard 83 | * Perkenalan VIDEO SELESAI DIBUAT 84 | * New MoodBoard VIDEO SELESAI DIBUAT 85 | * Color Scheme VIDEO SELESAI DIBUAT 86 | * Icon Style VIDEO SELESAI DIBUAT 87 | * Illustration Style VIDEO SELESAI DIBUAT 88 | * Tipography VIDEO SELESAI DIBUAT 89 | * Transformasi Wireframe ke Visual Design VIDEO SELESAI DIBUAT 90 | * Pengenalan Figma VIDEO SELESAI DIBUAT 91 | * Landing Page VIDEO SELESAI DIBUAT 92 | * banner VIDEO SELESAI DIBUAT 93 | * navigation header VIDEO SELESAI DIBUAT 94 | * items featured VIDEO SELESAI DIBUAT 95 | * testimonial VIDEO SELESAI DIBUAT 96 | * statistic VIDEO SELESAI DIBUAT 97 | * footer VIDEO SELESAI DIBUAT 98 | * Details Page VIDEO SELESAI DIBUAT 99 | * information kosan/hotel VIDEO SELESAI DIBUAT 100 | * booking now VIDEO SELESAI DIBUAT 101 | * features kosan VIDEO SELESAI DIBUAT 102 | * testimonial VIDEO SELESAI DIBUAT 103 | * Checkout Page VIDEO SELESAI DIBUAT 104 | * form request book VIDEO SELESAI DIBUAT 105 | * information orders VIDEO SELESAI DIBUAT 106 | 107 | ## Interaction Design IxD (dibawakan oleh Angga) 108 | * Micro Interaction Design VIDEO SELESAI DIBUAT 109 | * UI Animation VIDEO SELESAI DIBUAT 110 | * UI Prototype VIDEO SELESAI DIBUAT 111 | * Team Collaborate (bersama Front-End) VIDEO SELESAI DIBUAT 112 | * Meeting online VIDEO SELESAI DIBUAT 113 | * Hasil Feedback VIDEO SELESAI DIBUAT 114 | 115 | ## Design Handoff (dibawakan oleh Angga) 116 | * Mempersiapkan Assets VIDEO SELESAI DIBUAT 117 | * Slicing Icon, Illustration, etc. VIDEO SELESAI DIBUAT 118 | * Kolaborasi dengan Developer VIDEO SELESAI DIBUAT 119 | * CSS Inspect VIDEO SELESAI DIBUAT 120 | * Support Developer VIDEO SELESAI DIBUAT 121 | 122 | # Front-End Development (dibawakan oleh Yein) 123 | * Memahami kebutuhan user VIDEO SELESAI DIBUAT 124 | * PRD (product requirements development) dengan backend guy VIDEO SELESAI DIBUAT 125 | * Mempersiapkan reactjs + redux environment dan minimal tools yg harus di install VIDEO SELESAI DIBUAT 126 | * Mempersiapkan folder structure VIDEO SELESAI DIBUAT 127 | * Memindai components untuk smart-components (elements) dan dumb-components (parts) VIDEO SELESAI DIBUAT 128 | * Memulai Slicing Mockup ke Reactjs components (Scenes) 129 | * Landing Page VIDEO SELESAI DIBUAT 130 | * banner VIDEO SELESAI DIBUAT 131 | * navigation header VIDEO SELESAI DIBUAT 132 | * items featured VIDEO SELESAI DIBUAT 133 | * testimonial VIDEO SELESAI DIBUAT 134 | * features VIDEO SELESAI DIBUAT 135 | * footer VIDEO SELESAI DIBUAT 136 | * Details Page 137 | * information kosan/hotel VIDEO SELESAI DIBUAT 138 | * booking now VIDEO SELESAI DIBUAT 139 | * features kosan VIDEO SELESAI DIBUAT 140 | * testimonial VIDEO SELESAI DIBUAT 141 | * Checkout Page 142 | * form request book VIDEO SELESAI DIBUAT 143 | * information orders VIDEO SELESAI DIBUAT 144 | * Integrasi Scene Components dengan API 145 | * Landing Page VIDEO SELESAI DIBUAT 146 | * Diskusi dengan Backend team VIDEO SELESAI DIBUAT 147 | * Listing dynamic banner VIDEO SELESAI DIBUAT 148 | * Listing items featured VIDEO SELESAI DIBUAT 149 | * Listing testimonial VIDEO SELESAI DIBUAT 150 | * Listing Features VIDEO SELESAI DIBUAT 151 | * Details Page 152 | * Diskusi dengan Backend team VIDEO SELESAI DIBUAT 153 | * Menampilkan informasi kosan/hotel VIDEO SELESAI DIBUAT 154 | * navigate booking now -> checkout page VIDEO SELESAI DIBUAT 155 | * listing features kosan VIDEO SELESAI DIBUAT 156 | * listing testimonial VIDEO SELESAI DIBUAT 157 | Checkout Page 158 | * form request book validation VIDEO SELESAI DIBUAT 159 | * request API to create new booking VIDEO SELESAI DIBUAT 160 | * menampilkan feedback dari hasil request API create VIDEO SELESAI DIBUAT 161 | 162 | 163 | # Back-End Development (dibawakan oleh Elfin) 164 | * Persiapan Tools VIDEO SELESAI DIBUAT 165 | * Pengenalan Node JS VIDEO SELESAI DIBUAT 166 | * Memulai setup project NodeJS 167 | * Instalasi Node JS VIDEO SELESAI DIBUAT 168 | * Membuat projek di NodeJS dengan NPM init VIDEO SELESAI DIBUAT 169 | * Install Express JS with Node JS VIDEO SELESAI DIBUAT 170 | * Belajar Menangani Routing dan Instalasi Nodemon (Development) VIDEO SELESAI DIBUAT 171 | * Instalasi template engine EJS VIDEO SELESAI DIBUAT 172 | * Belajar Parsing Data VIDEO SELESAI DIBUAT 173 | * Pengenalan MongoDB dan Mongoose 174 | * Instalasi MongoDB VIDEO SELESAI DIBUAT 175 | * Mengenal Mongoose dan Create Data VIDEO SELESAI DIBUAT 176 | * Membaca data dari database mongodb menggunakan mongoose VIDEO SELESAI DIBUAT 177 | * Validasi data menggunakan mongoose VIDEO SELESAI DIBUAT 178 | * Update dan Delete data menggunakan Mongoose VIDEO SELESAI DIBUAT 179 | * Membuat relationship dan memasukkan data menggunakan mongoose VIDEO SELESAI DIBUAT 180 | * Memulai setup project (Admin) 181 | * Membuat projek baru dengan Express Generator VIDEO SELESAI DIBUAT 182 | * Setup project Express JS VIDEO SELESAI DIBUAT 183 | * Membuat tampilan dengan EJS Template Engine VIDEO SELESAI DIBUAT 184 | * Instalasi sbadmin2 dan konfigurasi template admin VIDEO SELESAI DIBUAT 185 | * View Login User 186 | * View Dashboard admin VIDEO SELESAI DIBUAT 187 | * View Item VIDEO SELESAI DIBUAT 188 | * View Booking VIDEO SELESAI DIBUAT 189 | * View Bank VIDEO SELESAI DIBUAT 190 | * Membuat model database dengan MongoDB VIDEO SELESAI DIBUAT 191 | * Konfigurasi MongoDB VIDEO SELESAI DIBUAT 192 | * Membuat CRUD Category (admin) VIDEO SELESAI DIBUAT 193 | * Handle Error & Validasi Input VIDEO SELESAI DIBUAT 194 | * Alert Message (connect-flash, express-session) & Title Dinamis VIDEO SELESAI DIBUAT 195 | * Membuat CRUD Bank(admin) VIDEO SELESAI DIBUAT 196 | * Membuat dashboard (admin) VIDEO SELESAI DIBUAT 197 | * Membuat CRUD Item (admin) VIDEO SELESAI DIBUAT 198 | * Membuat CRUD Feature (admin) VIDEO SELESAI DIBUAT 199 | * Membuat CRUD Activity (admin) VIDEO SELESAI DIBUAT 200 | * Membuat booking (admin) VIDEO SELESAI DIBUAT 201 | * Autentikasi VIDEO SELESAI DIBUAT 202 | * Membuat login (admin) VIDEO SELESAI DIBUAT 203 | * Memulai membuat API (Member) 204 | * Landing Page 205 | * endpoint get all landing page VIDEO SELESAI DIBUAT 206 | * menguji endpoint dengan postman / insomnia VIDEO SELESAI DIBUAT 207 | * Details Page 208 | * endpoint get detail page VIDEO SELESAI DIBUAT 209 | * menguji endpoint dengan postman / insomnia VIDEO SELESAI DIBUAT 210 | * Checkout Page 211 | * endpoint post checkout VIDEO SELESAI DIBUAT 212 | * menguji endpoint dengan postman / insomnia VIDEO SELESAI DIBUAT 213 | * Membuat dokumentasi Rest API dengan Postman 214 | * Landing Page VIDEO SELESAI DIBUAT 215 | * Details Page VIDEO SELESAI DIBUAT 216 | * Checkout Page VIDEO SELESAI DIBUAT 217 | * Unit Testing dengan menggunakan Mocha dan Chai 218 | * Landing Page VIDEO SELESAI DIBUAT 219 | * Details Page VIDEO SELESAI DIBUAT 220 | * Checkout Page VIDEO SELESAI DIBUAT 221 | 222 | 223 | # Website Deployment (VPS) 224 | * Persiapan Tools VIDEO SELESAI DIBUAT 225 | * Create akun heroku VIDEO SELESAI DIBUAT 226 | * Membuat repository github VIDEO SELESAI DIBUAT 227 | * Membuat akun mongodb atlas VIDEO SELESAI DIBUAT 228 | * Deploy ke Heroku dan Automated Deploy (Back-End) VIDEO SELESAI DIBUAT 229 | 230 | 231 | Diskon potongan harga 50.000 khusus untuk pelajar/mahasiswas selama masa preorder. Kirimkan foto kartu pelajar/kartu mahasiswa ketika melakukan konfirmasi pembayaran di whatsapp. Terima kasih. 232 | 233 | Tata Cara Order 234 | Harga Pre Order: 235 | Rp. 290.000 (10 Maret - 30 April) 236 | cukup transfer Rp. 240.000 khusus pelajar/mahasiswa selama masa preorder 237 | 238 | Harga Normal (Setelah Kelas di Publish / Rilis): 239 | Rp. 690.000 (1 Mei 2020 Kelas Dirilis) 240 | *bayar sekali, akses video dan konsultasi selamanya 241 | 242 | 243 | Silahkan transfer uang sebesar nominal di atas kepada rekening: 244 | 245 | BCA 8270785396 a/n I Ketut Suyasa Narayana 246 | 247 | BNI 0949079370 a/n I Ketut Suyasa Narayana 248 | 249 | Keterangan Transfer: BWAMERNNamaAnda 250 | (Jika transfer via ATM tidak perlu pakai keterangan) 251 | 252 | Setelah transfer silahkan konfirmasi foto pembayaran dan berikan data diri berupa: 253 | 1. Nama Lengkap 254 | 2. Email (@gmail) 255 | 3. Username Telegram 256 | (mohon setup username telegram untuk mempermuda proses invite) 257 | Foto Bukti Transfer 258 | untuk pelajar/mahasiswa sertakan KTM/tanda pelajar 259 | 260 | Kirim data diri dan bukti transfer ke WhatsApp : 261 | * 0859 5531 6973 262 | * Novita (Admin) 263 | * (Jam 08.00 - 20.00 WIB) 264 | 265 | Kami akan balas maksimal 1x24 jam chat kamu. 266 | Terima kasih dan kami tunggu di kelas! :) 267 | 268 | Catatan Penting: 269 | 1. Video akan diletakan pada Google Drive dan bebas akses selamanya dan dapat di-download untuk belajar secara offline 270 | 2. Kelas akan dapat diakses melalui platform BWA 271 | Dengan mengikuti kelas online ini, kalian mendapatkan akses ke serangkaian video pembelajaran sesuai dengan materi yang sudah ditulis di atas 272 | 3. Proses belajar dilakukan secara mandiri setelah kalian mendapatkan akses ke video pembelajaran 273 | 4. Kelas online ini tidak berupa pembelajaran secara live streaming 274 | 5. Video pembelajaran berbahasa Indonesia 275 | 6. Materi dari Dasar (tidak perlu khawatir, semua bisa belajar) 276 | 7. Semua tools yang diperlukan gratis / open-source 277 | 278 | 279 | ### Info Lebih Lengkap 280 | Website : [www.buildwithangga.com](https://buildwithangga.com/) 281 | Instagram : [@codeathome](https://www.instagram.com/codeathome/) 282 | WhatsApp : [085955316973](https://wa.me/6285955316973) 283 | github backend : [codeathome-dev](https://github.com/codeathome-dev) 284 | --- 285 | 286 | 287 | 288 | ###### tags: `mern` `classbwa` `codeathome` 289 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | var logger = require('morgan'); 6 | const methodOverride = require('method-override'); 7 | const session = require('express-session'); 8 | const flash = require('connect-flash'); 9 | const cors = require('cors'); 10 | // import mongoose 11 | const mongoose = require('mongoose'); 12 | mongoose.connect('mongodb+srv://codeathome:bwamern@cluster0-40j6e.mongodb.net/db_staycation?retryWrites=true&w=majority', { 13 | useNewUrlParser: true, 14 | useUnifiedTopology: true, 15 | useCreateIndex: true, 16 | useFindAndModify: false, 17 | }); 18 | 19 | var indexRouter = require('./routes/index'); 20 | var usersRouter = require('./routes/users'); 21 | // router admin 22 | const adminRouter = require('./routes/admin'); 23 | const apiRouter = require('./routes/api'); 24 | 25 | var app = express(); 26 | 27 | app.use(cors()); 28 | // view engine setup 29 | app.set('views', path.join(__dirname, 'views')); 30 | app.set('view engine', 'ejs'); 31 | app.use(methodOverride('_method')); 32 | app.use(session({ 33 | secret: 'keyboard cat', 34 | resave: false, 35 | saveUninitialized: true, 36 | cookie: { maxAge: 60000 } 37 | })); 38 | app.use(flash()); 39 | app.use(logger('dev')); 40 | app.use(express.json()); 41 | app.use(express.urlencoded({ extended: false })); 42 | app.use(cookieParser()); 43 | app.use(express.static(path.join(__dirname, 'public'))); 44 | app.use('/sb-admin-2', express.static(path.join(__dirname, 'node_modules/startbootstrap-sb-admin-2'))); 45 | 46 | app.use('/', indexRouter); 47 | app.use('/users', usersRouter); 48 | // admin 49 | app.use('/admin', adminRouter); 50 | app.use('/api/v1/member', apiRouter); 51 | 52 | // catch 404 and forward to error handler 53 | app.use(function (req, res, next) { 54 | next(createError(404)); 55 | }); 56 | 57 | // error handler 58 | app.use(function (err, req, res, next) { 59 | // set locals, only providing error in development 60 | res.locals.message = err.message; 61 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 62 | 63 | // render the error page 64 | res.status(err.status || 500); 65 | res.render('error'); 66 | }); 67 | 68 | module.exports = app; 69 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('server-staycation:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port, () => console.log(`Server on port ${port}`)); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /controllers/adminController.js: -------------------------------------------------------------------------------- 1 | const Category = require('../models/Category'); 2 | const Bank = require('../models/Bank'); 3 | const Item = require('../models/Item'); 4 | const Image = require('../models/Image'); 5 | const Feature = require('../models/Feature'); 6 | const Activity = require('../models/Activity'); 7 | const Booking = require('../models/Booking'); 8 | const Member = require('../models/Member'); 9 | const Users = require('../models/Users'); 10 | const fs = require('fs-extra'); 11 | const path = require('path'); 12 | const bcrypt = require('bcryptjs') 13 | 14 | module.exports = { 15 | viewSignin: async (req, res) => { 16 | try { 17 | const alertMessage = req.flash('alertMessage'); 18 | const alertStatus = req.flash('alertStatus'); 19 | const alert = { message: alertMessage, status: alertStatus }; 20 | if (req.session.user == null || req.session.user == undefined) { 21 | res.render('index', { 22 | alert, 23 | title: "Staycation | Login" 24 | }); 25 | } else { 26 | res.redirect('/admin/dashboard'); 27 | } 28 | } catch (error) { 29 | res.redirect('/admin/signin'); 30 | } 31 | }, 32 | 33 | actionSignin: async (req, res) => { 34 | try { 35 | const { username, password } = req.body; 36 | const user = await Users.findOne({ username: username }); 37 | if (!user) { 38 | req.flash('alertMessage', 'User yang anda masukan tidak ada!!'); 39 | req.flash('alertStatus', 'danger'); 40 | res.redirect('/admin/signin'); 41 | } 42 | const isPasswordMatch = await bcrypt.compare(password, user.password); 43 | if (!isPasswordMatch) { 44 | req.flash('alertMessage', 'Password yang anda masukan tidak cocok!!'); 45 | req.flash('alertStatus', 'danger'); 46 | res.redirect('/admin/signin'); 47 | } 48 | 49 | req.session.user = { 50 | id: user.id, 51 | username: user.username, 52 | role: user.role 53 | } 54 | 55 | res.redirect('/admin/dashboard'); 56 | 57 | } catch (error) { 58 | res.redirect('/admin/signin'); 59 | } 60 | }, 61 | 62 | actionLogout: (req, res) => { 63 | req.session.destroy(); 64 | res.redirect('/admin/signin'); 65 | }, 66 | 67 | viewDashboard: async (req, res) => { 68 | try { 69 | const member = await Member.find(); 70 | const booking = await Booking.find(); 71 | const item = await Item.find(); 72 | res.render('admin/dashboard/view_dashboard', { 73 | title: "Staycation | Dashboard", 74 | user: req.session.user, 75 | member, 76 | booking, 77 | item 78 | }); 79 | } catch (error) { 80 | res.redirect('/admin/dashboard'); 81 | } 82 | }, 83 | 84 | viewCategory: async (req, res) => { 85 | try { 86 | const category = await Category.find(); 87 | const alertMessage = req.flash('alertMessage'); 88 | const alertStatus = req.flash('alertStatus'); 89 | const alert = { message: alertMessage, status: alertStatus }; 90 | res.render('admin/category/view_category', { 91 | category, 92 | alert, 93 | title: "Staycation | Category", 94 | user: req.session.user 95 | }); 96 | } catch (error) { 97 | res.redirect('/admin/category'); 98 | } 99 | }, 100 | 101 | addCategory: async (req, res) => { 102 | try { 103 | const { name } = req.body; 104 | // console.log(name); 105 | await Category.create({ name }); 106 | req.flash('alertMessage', 'Success Add Category'); 107 | req.flash('alertStatus', 'success'); 108 | res.redirect('/admin/category'); 109 | } catch (error) { 110 | req.flash('alertMessage', `${error.message}`); 111 | req.flash('alertStatus', 'danger'); 112 | res.redirect('/admin/category'); 113 | } 114 | }, 115 | 116 | editCategory: async (req, res) => { 117 | try { 118 | const { id, name } = req.body; 119 | const category = await Category.findOne({ _id: id }); 120 | category.name = name; 121 | await category.save(); 122 | req.flash('alertMessage', 'Success Update Category'); 123 | req.flash('alertStatus', 'success'); 124 | res.redirect('/admin/category'); 125 | } catch (error) { 126 | req.flash('alertMessage', `${error.message}`); 127 | req.flash('alertStatus', 'danger'); 128 | res.redirect('/admin/category'); 129 | } 130 | }, 131 | 132 | deleteCategory: async (req, res) => { 133 | try { 134 | const { id } = req.params; 135 | const category = await Category.findOne({ _id: id }); 136 | await category.remove(); 137 | req.flash('alertMessage', 'Success Delete Category'); 138 | req.flash('alertStatus', 'success'); 139 | res.redirect('/admin/category'); 140 | } catch (error) { 141 | req.flash('alertMessage', `${error.message}`); 142 | req.flash('alertStatus', 'danger'); 143 | res.redirect('/admin/category'); 144 | } 145 | }, 146 | 147 | viewBank: async (req, res) => { 148 | try { 149 | const bank = await Bank.find(); 150 | const alertMessage = req.flash('alertMessage'); 151 | const alertStatus = req.flash('alertStatus'); 152 | const alert = { message: alertMessage, status: alertStatus }; 153 | res.render('admin/bank/view_bank', { 154 | title: "Staycation | Bank", 155 | alert, 156 | bank, 157 | user: req.session.user 158 | }); 159 | } catch (error) { 160 | req.flash('alertMessage', `${error.message}`); 161 | req.flash('alertStatus', 'danger'); 162 | res.redirect('/admin/bank') 163 | } 164 | }, 165 | 166 | addBank: async (req, res) => { 167 | try { 168 | const { name, nameBank, nomorRekening } = req.body; 169 | await Bank.create({ 170 | name, 171 | nameBank, 172 | nomorRekening, 173 | imageUrl: `images/${req.file.filename}` 174 | }); 175 | req.flash('alertMessage', 'Success Add Bank'); 176 | req.flash('alertStatus', 'success'); 177 | res.redirect('/admin/bank'); 178 | } catch (error) { 179 | req.flash('alertMessage', `${error.message}`); 180 | req.flash('alertStatus', 'danger'); 181 | res.redirect('/admin/bank'); 182 | } 183 | }, 184 | 185 | editBank: async (req, res) => { 186 | try { 187 | const { id, name, nameBank, nomorRekening } = req.body; 188 | const bank = await Bank.findOne({ _id: id }); 189 | if (req.file == undefined) { 190 | bank.name = name; 191 | bank.nameBank = nameBank; 192 | bank.nomorRekening = nomorRekening; 193 | await bank.save(); 194 | req.flash('alertMessage', 'Success Update Bank'); 195 | req.flash('alertStatus', 'success'); 196 | res.redirect('/admin/bank'); 197 | } else { 198 | await fs.unlink(path.join(`public/${bank.imageUrl}`)); 199 | bank.name = name; 200 | bank.nameBank = nameBank; 201 | bank.nomorRekening = nomorRekening; 202 | bank.imageUrl = `images/${req.file.filename}` 203 | await bank.save(); 204 | req.flash('alertMessage', 'Success Update Bank'); 205 | req.flash('alertStatus', 'success'); 206 | res.redirect('/admin/bank'); 207 | } 208 | } catch (error) { 209 | req.flash('alertMessage', `${error.message}`); 210 | req.flash('alertStatus', 'danger'); 211 | res.redirect('/admin/bank'); 212 | } 213 | }, 214 | 215 | deleteBank: async (req, res) => { 216 | try { 217 | const { id } = req.params; 218 | const bank = await Bank.findOne({ _id: id }); 219 | await fs.unlink(path.join(`public/${bank.imageUrl}`)); 220 | await bank.remove(); 221 | req.flash('alertMessage', 'Success Delete Bank'); 222 | req.flash('alertStatus', 'success'); 223 | res.redirect('/admin/bank'); 224 | } catch (error) { 225 | req.flash('alertMessage', `${error.message}`); 226 | req.flash('alertStatus', 'danger'); 227 | res.redirect('/admin/bank'); 228 | } 229 | }, 230 | 231 | viewItem: async (req, res) => { 232 | try { 233 | const item = await Item.find() 234 | .populate({ path: 'imageId', select: 'id imageUrl' }) 235 | .populate({ path: 'categoryId', select: 'id name' }); 236 | 237 | const category = await Category.find(); 238 | const alertMessage = req.flash('alertMessage'); 239 | const alertStatus = req.flash('alertStatus'); 240 | const alert = { message: alertMessage, status: alertStatus }; 241 | res.render('admin/item/view_item', { 242 | title: "Staycation | Item", 243 | category, 244 | alert, 245 | item, 246 | action: 'view', 247 | user: req.session.user 248 | }); 249 | } catch (error) { 250 | req.flash('alertMessage', `${error.message}`); 251 | req.flash('alertStatus', 'danger'); 252 | res.redirect('/admin/item'); 253 | } 254 | }, 255 | 256 | addItem: async (req, res) => { 257 | try { 258 | const { categoryId, title, price, city, about } = req.body; 259 | if (req.files.length > 0) { 260 | const category = await Category.findOne({ _id: categoryId }); 261 | const newItem = { 262 | categoryId, 263 | title, 264 | description: about, 265 | price, 266 | city 267 | } 268 | const item = await Item.create(newItem); 269 | category.itemId.push({ _id: item._id }); 270 | await category.save(); 271 | for (let i = 0; i < req.files.length; i++) { 272 | const imageSave = await Image.create({ imageUrl: `images/${req.files[i].filename}` }); 273 | item.imageId.push({ _id: imageSave._id }); 274 | await item.save(); 275 | } 276 | req.flash('alertMessage', 'Success Add Item'); 277 | req.flash('alertStatus', 'success'); 278 | res.redirect('/admin/item'); 279 | } 280 | } catch (error) { 281 | req.flash('alertMessage', `${error.message}`); 282 | req.flash('alertStatus', 'danger'); 283 | res.redirect('/admin/item'); 284 | } 285 | }, 286 | 287 | showImageItem: async (req, res) => { 288 | try { 289 | const { id } = req.params; 290 | const item = await Item.findOne({ _id: id }) 291 | .populate({ path: 'imageId', select: 'id imageUrl' }); 292 | const alertMessage = req.flash('alertMessage'); 293 | const alertStatus = req.flash('alertStatus'); 294 | const alert = { message: alertMessage, status: alertStatus }; 295 | res.render('admin/item/view_item', { 296 | title: "Staycation | Show Image Item", 297 | alert, 298 | item, 299 | action: 'show image', 300 | user: req.session.user 301 | }); 302 | } catch (error) { 303 | req.flash('alertMessage', `${error.message}`); 304 | req.flash('alertStatus', 'danger'); 305 | res.redirect('/admin/item'); 306 | } 307 | }, 308 | 309 | showEditItem: async (req, res) => { 310 | try { 311 | const { id } = req.params; 312 | const item = await Item.findOne({ _id: id }) 313 | .populate({ path: 'imageId', select: 'id imageUrl' }) 314 | .populate({ path: 'categoryId', select: 'id name' }); 315 | 316 | console.log(item) 317 | const category = await Category.find(); 318 | // console.log(category) 319 | const alertMessage = req.flash('alertMessage'); 320 | const alertStatus = req.flash('alertStatus'); 321 | const alert = { message: alertMessage, status: alertStatus }; 322 | res.render('admin/item/view_item', { 323 | title: "Staycation | Edit Item", 324 | alert, 325 | item, 326 | category, 327 | action: 'edit', 328 | user: req.session.user 329 | }); 330 | } catch (error) { 331 | console.log(error) 332 | req.flash('alertMessage', `${error.message}`); 333 | req.flash('alertStatus', 'danger'); 334 | res.redirect('/admin/item'); 335 | } 336 | }, 337 | 338 | editItem: async (req, res) => { 339 | try { 340 | const { id } = req.params; 341 | const { categoryId, title, price, city, about } = req.body; 342 | const item = await Item.findOne({ _id: id }) 343 | .populate({ path: 'imageId', select: 'id imageUrl' }) 344 | .populate({ path: 'categoryId', select: 'id name' }); 345 | 346 | if (req.files.length > 0) { 347 | for (let i = 0; i < item.imageId.length; i++) { 348 | const imageUpdate = await Image.findOne({ _id: item.imageId[i]._id }); 349 | await fs.unlink(path.join(`public/${imageUpdate.imageUrl}`)); 350 | imageUpdate.imageUrl = `images/${req.files[i].filename}`; 351 | await imageUpdate.save(); 352 | } 353 | item.title = title; 354 | item.price = price; 355 | item.city = city; 356 | item.description = about; 357 | item.categoryId = categoryId; 358 | await item.save(); 359 | req.flash('alertMessage', 'Success update Item'); 360 | req.flash('alertStatus', 'success'); 361 | res.redirect('/admin/item'); 362 | } else { 363 | item.title = title; 364 | item.price = price; 365 | item.city = city; 366 | item.description = about; 367 | item.categoryId = categoryId; 368 | await item.save(); 369 | req.flash('alertMessage', 'Success update Item'); 370 | req.flash('alertStatus', 'success'); 371 | res.redirect('/admin/item'); 372 | } 373 | } catch (error) { 374 | req.flash('alertMessage', `${error.message}`); 375 | req.flash('alertStatus', 'danger'); 376 | res.redirect('/admin/item'); 377 | } 378 | }, 379 | 380 | deleteItem: async (req, res) => { 381 | try { 382 | const { id } = req.params; 383 | const item = await Item.findOne({ _id: id }).populate('imageId'); 384 | for (let i = 0; i < item.imageId.length; i++) { 385 | Image.findOne({ _id: item.imageId[i]._id }).then((image) => { 386 | fs.unlink(path.join(`public/${image.imageUrl}`)); 387 | image.remove(); 388 | }).catch((error) => { 389 | req.flash('alertMessage', `${error.message}`); 390 | req.flash('alertStatus', 'danger'); 391 | res.redirect('/admin/item'); 392 | }); 393 | } 394 | await item.remove(); 395 | req.flash('alertMessage', 'Success delete Item'); 396 | req.flash('alertStatus', 'success'); 397 | res.redirect('/admin/item'); 398 | } catch (error) { 399 | req.flash('alertMessage', `${error.message}`); 400 | req.flash('alertStatus', 'danger'); 401 | res.redirect('/admin/item'); 402 | } 403 | }, 404 | 405 | viewDetailItem: async (req, res) => { 406 | const { itemId } = req.params; 407 | try { 408 | const alertMessage = req.flash('alertMessage'); 409 | const alertStatus = req.flash('alertStatus'); 410 | const alert = { message: alertMessage, status: alertStatus }; 411 | 412 | const feature = await Feature.find({ itemId: itemId }); 413 | const activity = await Activity.find({ itemId: itemId }); 414 | 415 | res.render('admin/item/detail_item/view_detail_item', { 416 | title: 'Staycation | Detail Item', 417 | alert, 418 | itemId, 419 | feature, 420 | activity, 421 | user: req.session.user 422 | }) 423 | 424 | } catch (error) { 425 | req.flash('alertMessage', `${error.message}`); 426 | req.flash('alertStatus', 'danger'); 427 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 428 | } 429 | }, 430 | addFeature: async (req, res) => { 431 | const { name, qty, itemId } = req.body; 432 | 433 | try { 434 | if (!req.file) { 435 | req.flash('alertMessage', 'Image not found'); 436 | req.flash('alertStatus', 'danger'); 437 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 438 | } 439 | const feature = await Feature.create({ 440 | name, 441 | qty, 442 | itemId, 443 | imageUrl: `images/${req.file.filename}` 444 | }); 445 | 446 | const item = await Item.findOne({ _id: itemId }); 447 | item.featureId.push({ _id: feature._id }); 448 | await item.save() 449 | req.flash('alertMessage', 'Success Add Feature'); 450 | req.flash('alertStatus', 'success'); 451 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 452 | } catch (error) { 453 | req.flash('alertMessage', `${error.message}`); 454 | req.flash('alertStatus', 'danger'); 455 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 456 | } 457 | }, 458 | 459 | editFeature: async (req, res) => { 460 | const { id, name, qty, itemId } = req.body; 461 | try { 462 | const feature = await Feature.findOne({ _id: id }); 463 | if (req.file == undefined) { 464 | feature.name = name; 465 | feature.qty = qty; 466 | await feature.save(); 467 | req.flash('alertMessage', 'Success Update Feature'); 468 | req.flash('alertStatus', 'success'); 469 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 470 | } else { 471 | await fs.unlink(path.join(`public/${feature.imageUrl}`)); 472 | feature.name = name; 473 | feature.qty = qty; 474 | feature.imageUrl = `images/${req.file.filename}` 475 | await feature.save(); 476 | req.flash('alertMessage', 'Success Update Feature'); 477 | req.flash('alertStatus', 'success'); 478 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 479 | } 480 | } catch (error) { 481 | req.flash('alertMessage', `${error.message}`); 482 | req.flash('alertStatus', 'danger'); 483 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 484 | } 485 | }, 486 | 487 | deleteFeature: async (req, res) => { 488 | const { id, itemId } = req.params; 489 | try { 490 | const feature = await Feature.findOne({ _id: id }); 491 | 492 | const item = await Item.findOne({ _id: itemId }).populate('featureId'); 493 | for (let i = 0; i < item.featureId.length; i++) { 494 | if (item.featureId[i]._id.toString() === feature._id.toString()) { 495 | item.featureId.pull({ _id: feature._id }); 496 | await item.save(); 497 | } 498 | } 499 | await fs.unlink(path.join(`public/${feature.imageUrl}`)); 500 | await feature.remove(); 501 | req.flash('alertMessage', 'Success Delete Feature'); 502 | req.flash('alertStatus', 'success'); 503 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 504 | } catch (error) { 505 | req.flash('alertMessage', `${error.message}`); 506 | req.flash('alertStatus', 'danger'); 507 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 508 | } 509 | }, 510 | 511 | addActivity: async (req, res) => { 512 | const { name, type, itemId } = req.body; 513 | 514 | try { 515 | if (!req.file) { 516 | req.flash('alertMessage', 'Image not found'); 517 | req.flash('alertStatus', 'danger'); 518 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 519 | } 520 | const activity = await Activity.create({ 521 | name, 522 | type, 523 | itemId, 524 | imageUrl: `images/${req.file.filename}` 525 | }); 526 | 527 | const item = await Item.findOne({ _id: itemId }); 528 | item.activityId.push({ _id: activity._id }); 529 | await item.save() 530 | req.flash('alertMessage', 'Success Add Activity'); 531 | req.flash('alertStatus', 'success'); 532 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 533 | } catch (error) { 534 | req.flash('alertMessage', `${error.message}`); 535 | req.flash('alertStatus', 'danger'); 536 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 537 | } 538 | }, 539 | 540 | editActivity: async (req, res) => { 541 | const { id, name, type, itemId } = req.body; 542 | try { 543 | const activity = await Activity.findOne({ _id: id }); 544 | if (req.file == undefined) { 545 | activity.name = name; 546 | activity.type = type; 547 | await activity.save(); 548 | req.flash('alertMessage', 'Success Update activity'); 549 | req.flash('alertStatus', 'success'); 550 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 551 | } else { 552 | await fs.unlink(path.join(`public/${activity.imageUrl}`)); 553 | activity.name = name; 554 | activity.type = type; 555 | activity.imageUrl = `images/${req.file.filename}` 556 | await activity.save(); 557 | req.flash('alertMessage', 'Success Update activity'); 558 | req.flash('alertStatus', 'success'); 559 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 560 | } 561 | } catch (error) { 562 | req.flash('alertMessage', `${error.message}`); 563 | req.flash('alertStatus', 'danger'); 564 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 565 | } 566 | }, 567 | 568 | deleteActivity: async (req, res) => { 569 | const { id, itemId } = req.params; 570 | try { 571 | const activity = await Activity.findOne({ _id: id }); 572 | 573 | const item = await Item.findOne({ _id: itemId }).populate('activityId'); 574 | for (let i = 0; i < item.activityId.length; i++) { 575 | if (item.activityId[i]._id.toString() === activity._id.toString()) { 576 | item.activityId.pull({ _id: activity._id }); 577 | await item.save(); 578 | } 579 | } 580 | await fs.unlink(path.join(`public/${activity.imageUrl}`)); 581 | await activity.remove(); 582 | req.flash('alertMessage', 'Success Delete Activity'); 583 | req.flash('alertStatus', 'success'); 584 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 585 | } catch (error) { 586 | req.flash('alertMessage', `${error.message}`); 587 | req.flash('alertStatus', 'danger'); 588 | res.redirect(`/admin/item/show-detail-item/${itemId}`); 589 | } 590 | }, 591 | 592 | viewBooking: async (req, res) => { 593 | try { 594 | const booking = await Booking.find() 595 | .populate('memberId') 596 | .populate('bankId'); 597 | 598 | res.render('admin/booking/view_booking', { 599 | title: "Staycation | Booking", 600 | user: req.session.user, 601 | booking 602 | }); 603 | } catch (error) { 604 | res.redirect('/admin/booking'); 605 | } 606 | }, 607 | 608 | showDetailBooking: async (req, res) => { 609 | const { id } = req.params 610 | try { 611 | const alertMessage = req.flash('alertMessage'); 612 | const alertStatus = req.flash('alertStatus'); 613 | const alert = { message: alertMessage, status: alertStatus }; 614 | 615 | const booking = await Booking.findOne({ _id: id }) 616 | .populate('memberId') 617 | .populate('bankId'); 618 | 619 | res.render('admin/booking/show_detail_booking', { 620 | title: "Staycation | Detail Booking", 621 | user: req.session.user, 622 | booking, 623 | alert 624 | }); 625 | } catch (error) { 626 | res.redirect('/admin/booking'); 627 | } 628 | }, 629 | 630 | actionConfirmation: async (req, res) => { 631 | const { id } = req.params; 632 | try { 633 | const booking = await Booking.findOne({ _id: id }); 634 | booking.payments.status = 'Accept'; 635 | await booking.save(); 636 | req.flash('alertMessage', 'Success Confirmation Pembayaran'); 637 | req.flash('alertStatus', 'success'); 638 | res.redirect(`/admin/booking/${id}`); 639 | } catch (error) { 640 | res.redirect(`/admin/booking/${id}`); 641 | } 642 | }, 643 | 644 | actionReject: async (req, res) => { 645 | const { id } = req.params; 646 | try { 647 | const booking = await Booking.findOne({ _id: id }); 648 | booking.payments.status = 'Reject'; 649 | await booking.save(); 650 | req.flash('alertMessage', 'Success Reject Pembayaran'); 651 | req.flash('alertStatus', 'success'); 652 | res.redirect(`/admin/booking/${id}`); 653 | } catch (error) { 654 | res.redirect(`/admin/booking/${id}`); 655 | } 656 | } 657 | }; -------------------------------------------------------------------------------- /controllers/apiController.js: -------------------------------------------------------------------------------- 1 | const Item = require('../models/Item'); 2 | const Treasure = require('../models/Activity'); 3 | const Treveler = require('../models/Booking'); 4 | const Category = require('../models/Category'); 5 | const Bank = require('../models/Bank'); 6 | const Booking = require('../models/Booking'); 7 | const Member = require('../models/Member'); 8 | 9 | module.exports = { 10 | landingPage: async (req, res) => { 11 | try { 12 | const mostPicked = await Item.find() 13 | .select('_id title country city price unit imageId') 14 | .limit(5) 15 | .populate({ path: 'imageId', select: '_id imageUrl' }) 16 | 17 | const category = await Category.find() 18 | .select('_id name') 19 | .limit(3) 20 | .populate({ 21 | path: 'itemId', 22 | select: '_id title country city isPopular imageId', 23 | perDocumentLimit: 4, 24 | options: { sort: { sumBooking: -1 } }, 25 | populate: { 26 | path: 'imageId', 27 | select: '_id imageUrl', 28 | perDocumentLimit: 1 29 | } 30 | }) 31 | 32 | const treveler = await Treveler.find(); 33 | const treasure = await Treasure.find(); 34 | const city = await Item.find(); 35 | 36 | for (let i = 0; i < category.length; i++) { 37 | for (let x = 0; x < category[i].itemId.length; x++) { 38 | const item = await Item.findOne({ _id: category[i].itemId[x]._id }); 39 | item.isPopular = false; 40 | await item.save(); 41 | if (category[i].itemId[0] === category[i].itemId[x]) { 42 | item.isPopular = true; 43 | await item.save(); 44 | } 45 | } 46 | } 47 | 48 | const testimonial = { 49 | _id: "asd1293uasdads1", 50 | imageUrl: "images/testimonial2.jpg", 51 | name: "Happy Family", 52 | rate: 4.55, 53 | content: "What a great trip with my family and I should try again next time soon ...", 54 | familyName: "Angga", 55 | familyOccupation: "Product Designer" 56 | } 57 | 58 | res.status(200).json({ 59 | hero: { 60 | travelers: treveler.length, 61 | treasures: treasure.length, 62 | cities: city.length 63 | }, 64 | mostPicked, 65 | category, 66 | testimonial 67 | }) 68 | } catch (error) { 69 | console.log(error); 70 | res.status(500).json({ message: "Internal server error" }); 71 | } 72 | }, 73 | 74 | detailPage: async (req, res) => { 75 | try { 76 | const { id } = req.params; 77 | const item = await Item.findOne({ _id: id }) 78 | .populate({ path: 'featureId', select: '_id name qty imageUrl' }) 79 | .populate({ path: 'activityId', select: '_id name type imageUrl' }) 80 | .populate({ path: 'imageId', select: '_id imageUrl' }); 81 | 82 | const bank = await Bank.find(); 83 | 84 | const testimonial = { 85 | _id: "asd1293uasdads1", 86 | imageUrl: "images/testimonial1.jpg", 87 | name: "Happy Family", 88 | rate: 4.55, 89 | content: "What a great trip with my family and I should try again next time soon ...", 90 | familyName: "Angga", 91 | familyOccupation: "Product Designer" 92 | } 93 | 94 | res.status(200).json({ 95 | ...item._doc, 96 | bank, 97 | testimonial 98 | }) 99 | 100 | } catch (error) { 101 | res.status(500).json({ message: "Internal server error" }); 102 | } 103 | }, 104 | 105 | bookingPage: async (req, res) => { 106 | const { 107 | idItem, 108 | duration, 109 | // price, 110 | bookingStartDate, 111 | bookingEndDate, 112 | firstName, 113 | lastName, 114 | email, 115 | phoneNumber, 116 | accountHolder, 117 | bankFrom, 118 | } = req.body; 119 | 120 | if (!req.file) { 121 | return res.status(404).json({ message: "Image not found" }); 122 | } 123 | 124 | console.log(idItem) 125 | 126 | if ( 127 | idItem === undefined || 128 | duration === undefined || 129 | // price === undefined || 130 | bookingStartDate === undefined || 131 | bookingEndDate === undefined || 132 | firstName === undefined || 133 | lastName === undefined || 134 | email === undefined || 135 | phoneNumber === undefined || 136 | accountHolder === undefined || 137 | bankFrom === undefined) { 138 | res.status(404).json({ message: "Lengkapi semua field" }); 139 | } 140 | 141 | const item = await Item.findOne({ _id: idItem }); 142 | 143 | if (!item) { 144 | return res.status(404).json({ message: "Item not found" }); 145 | } 146 | 147 | item.sumBooking += 1; 148 | 149 | await item.save(); 150 | 151 | let total = item.price * duration; 152 | let tax = total * 0.10; 153 | 154 | const invoice = Math.floor(1000000 + Math.random() * 9000000); 155 | 156 | const member = await Member.create({ 157 | firstName, 158 | lastName, 159 | email, 160 | phoneNumber 161 | }); 162 | 163 | const newBooking = { 164 | invoice, 165 | bookingStartDate, 166 | bookingEndDate, 167 | total: total += tax, 168 | itemId: { 169 | _id: item.id, 170 | title: item.title, 171 | price: item.price, 172 | duration: duration 173 | }, 174 | 175 | memberId: member.id, 176 | payments: { 177 | proofPayment: `images/${req.file.filename}`, 178 | bankFrom: bankFrom, 179 | accountHolder: accountHolder 180 | } 181 | } 182 | 183 | const booking = await Booking.create(newBooking); 184 | 185 | res.status(201).json({ message: "Success Booking", booking }); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /middlewares/auth.js: -------------------------------------------------------------------------------- 1 | const isLogin = (req, res, next) => { 2 | if (req.session.user == null || req.session.user == undefined) { 3 | req.flash('alertMessage', 'Session telah habis silahkan signin kembali!!'); 4 | req.flash('alertStatus', 'danger'); 5 | res.redirect('/admin/signin'); 6 | } else { 7 | next(); 8 | } 9 | } 10 | 11 | module.exports = isLogin; -------------------------------------------------------------------------------- /middlewares/multer.js: -------------------------------------------------------------------------------- 1 | const multer = require("multer"); 2 | const path = require("path"); 3 | 4 | const storage = multer.diskStorage({ 5 | destination: "public/images", 6 | filename: function (req, file, cb) { 7 | cb(null, Date.now() + path.extname(file.originalname)); 8 | } 9 | }); 10 | 11 | const uploadSingle = multer({ 12 | storage: storage, 13 | // limits: { fileSize: 1000000 }, 14 | fileFilter: function (req, file, cb) { 15 | checkFileType(file, cb); 16 | } 17 | }).single("image"); 18 | 19 | const uploadMultiple = multer({ 20 | storage: storage, 21 | // limits: { fileSize: 1000000 }, 22 | fileFilter: function (req, file, cb) { 23 | checkFileType(file, cb); 24 | } 25 | }).array("image"); 26 | 27 | function checkFileType(file, cb) { 28 | const fileTypes = /jpeg|jpg|png|gif/; 29 | const extName = fileTypes.test(path.extname(file.originalname).toLowerCase()); 30 | const mimeType = fileTypes.test(file.mimetype); 31 | if (mimeType && extName) { 32 | return cb(null, true); 33 | } else { 34 | cb("Error: Images Only !!!"); 35 | } 36 | } 37 | module.exports = { uploadSingle, uploadMultiple }; -------------------------------------------------------------------------------- /models/Activity.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { ObjectId } = mongoose.Schema; 3 | 4 | const activitySchema = new mongoose.Schema({ 5 | name: { 6 | type: String, 7 | required: true 8 | }, 9 | type: { 10 | type: String, 11 | required: true 12 | }, 13 | imageUrl: { 14 | type: String, 15 | required: true 16 | }, 17 | isPopular: { 18 | type: Boolean 19 | }, 20 | itemId: { 21 | type: ObjectId, 22 | ref: 'Item' 23 | } 24 | }) 25 | 26 | module.exports = mongoose.model('Activity', activitySchema) -------------------------------------------------------------------------------- /models/Bank.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const bankSchema = new mongoose.Schema({ 4 | nameBank: { 5 | type: String, 6 | required: true 7 | }, 8 | nomorRekening: { 9 | type: String, 10 | required: true 11 | }, 12 | name: { 13 | type: String, 14 | required: true 15 | }, 16 | imageUrl: { 17 | type: String, 18 | required: true 19 | } 20 | }) 21 | 22 | module.exports = mongoose.model('Bank', bankSchema) -------------------------------------------------------------------------------- /models/Booking.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { ObjectId } = mongoose.Schema; 3 | 4 | const bookingSchema = new mongoose.Schema({ 5 | bookingStartDate: { 6 | type: Date, 7 | required: true 8 | }, 9 | bookingEndDate: { 10 | type: Date, 11 | required: true 12 | }, 13 | invoice: { 14 | type: String, 15 | required: true 16 | }, 17 | itemId: { 18 | _id: { 19 | type: ObjectId, 20 | ref: 'Item', 21 | required: true 22 | }, 23 | title: { 24 | type: String, 25 | required: true 26 | }, 27 | price: { 28 | type: Number, 29 | required: true 30 | }, 31 | duration: { 32 | type: Number, 33 | required: true 34 | } 35 | }, 36 | total: { 37 | type: Number, 38 | required: true 39 | }, 40 | memberId: { 41 | type: ObjectId, 42 | ref: 'Member' 43 | }, 44 | bankId: { 45 | type: ObjectId, 46 | ref: 'Bank' 47 | }, 48 | payments: { 49 | proofPayment: { 50 | type: String, 51 | required: true 52 | }, 53 | bankFrom: { 54 | type: String, 55 | required: true 56 | }, 57 | accountHolder: { 58 | type: String, 59 | required: true 60 | }, 61 | status: { 62 | type: String, 63 | default: 'Proses' 64 | } 65 | } 66 | }) 67 | 68 | module.exports = mongoose.model('Booking', bookingSchema) -------------------------------------------------------------------------------- /models/Category.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { ObjectId } = mongoose.Schema; 3 | 4 | const categorySchema = new mongoose.Schema({ 5 | name: { 6 | type: String, 7 | required: true 8 | }, 9 | itemId: [{ 10 | type: ObjectId, 11 | ref: 'Item' 12 | }] 13 | }) 14 | 15 | module.exports = mongoose.model('Category', categorySchema) -------------------------------------------------------------------------------- /models/Feature.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { ObjectId } = mongoose.Schema; 3 | 4 | const featureSchema = new mongoose.Schema({ 5 | name: { 6 | type: String, 7 | required: true 8 | }, 9 | qty: { 10 | type: Number, 11 | required: true 12 | }, 13 | imageUrl: { 14 | type: String, 15 | required: true 16 | }, 17 | itemId: { 18 | type: ObjectId, 19 | ref: 'Item' 20 | } 21 | }) 22 | 23 | module.exports = mongoose.model('Feature', featureSchema) -------------------------------------------------------------------------------- /models/Image.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const imageSchema = new mongoose.Schema({ 4 | imageUrl: { 5 | type: String, 6 | required: true 7 | } 8 | }) 9 | 10 | module.exports = mongoose.model('Image', imageSchema) -------------------------------------------------------------------------------- /models/Item.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { ObjectId } = mongoose.Schema; 3 | 4 | const itemSchema = new mongoose.Schema({ 5 | title: { 6 | type: String, 7 | required: true 8 | }, 9 | price: { 10 | type: Number, 11 | required: true 12 | }, 13 | country: { 14 | type: String, 15 | default: 'Indonesia' 16 | }, 17 | city: { 18 | type: String, 19 | required: true 20 | }, 21 | isPopular: { 22 | type: Boolean, 23 | default: false 24 | }, 25 | description: { 26 | type: String, 27 | required: true 28 | }, 29 | unit: { 30 | type: String, 31 | default: 'night' 32 | }, 33 | sumBooking: { 34 | type: Number, 35 | default: 0 36 | }, 37 | categoryId: { 38 | type: ObjectId, 39 | ref: 'Category' 40 | }, 41 | imageId: [{ 42 | type: ObjectId, 43 | ref: 'Image' 44 | }], 45 | featureId: [{ 46 | type: ObjectId, 47 | ref: 'Feature' 48 | }], 49 | activityId: [{ 50 | type: ObjectId, 51 | ref: 'Activity' 52 | }] 53 | }) 54 | 55 | module.exports = mongoose.model('Item', itemSchema) -------------------------------------------------------------------------------- /models/Member.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const memberSchema = new mongoose.Schema({ 4 | firstName: { 5 | type: String, 6 | required: true 7 | }, 8 | lastName: { 9 | type: String, 10 | required: true 11 | }, 12 | email: { 13 | type: String, 14 | required: true 15 | }, 16 | phoneNumber: { 17 | type: String, 18 | required: true 19 | } 20 | }) 21 | 22 | module.exports = mongoose.model('Member', memberSchema) -------------------------------------------------------------------------------- /models/Users.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const bcrypt = require("bcryptjs"); 3 | 4 | const usersSchema = new mongoose.Schema({ 5 | username: { 6 | type: String, 7 | required: true 8 | }, 9 | password: { 10 | type: String, 11 | required: true 12 | }, 13 | role: { 14 | type: String, 15 | required: true, 16 | default: 'guest' 17 | } 18 | }) 19 | 20 | usersSchema.pre('save', async function (next) { 21 | const user = this; 22 | if (user.isModified('password')) { 23 | user.password = await bcrypt.hash(user.password, 8); 24 | } 25 | }) 26 | 27 | module.exports = mongoose.model('Users', usersSchema) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-staycation", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www", 7 | "dev": "nodemon ./bin/www", 8 | "test": "mocha --exit" 9 | }, 10 | "dependencies": { 11 | "bcryptjs": "^2.4.3", 12 | "chai": "^4.2.0", 13 | "chai-http": "^4.3.0", 14 | "connect-flash": "^0.1.1", 15 | "cookie-parser": "~1.4.4", 16 | "cors": "^2.8.5", 17 | "debug": "~2.6.9", 18 | "ejs": "~2.6.1", 19 | "express": "~4.16.1", 20 | "express-session": "^1.17.0", 21 | "fs-extra": "^9.0.0", 22 | "http-errors": "~1.6.3", 23 | "method-override": "^3.0.0", 24 | "mocha": "^7.1.2", 25 | "mongoose": "^5.9.7", 26 | "mongoose-seed": "^0.6.0", 27 | "morgan": "~1.9.1", 28 | "multer": "^1.4.2", 29 | "startbootstrap-sb-admin-2": "^4.0.7" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/images/activity-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/activity-1.png -------------------------------------------------------------------------------- /public/images/activity-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/activity-2.png -------------------------------------------------------------------------------- /public/images/activity-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/activity-3.png -------------------------------------------------------------------------------- /public/images/activity-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/activity-4.png -------------------------------------------------------------------------------- /public/images/buktibayar.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/buktibayar.jpeg -------------------------------------------------------------------------------- /public/images/feature-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/feature-1.png -------------------------------------------------------------------------------- /public/images/feature-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/feature-2.png -------------------------------------------------------------------------------- /public/images/feature-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/feature-3.png -------------------------------------------------------------------------------- /public/images/feature-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/feature-4.png -------------------------------------------------------------------------------- /public/images/feature-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/feature-5.png -------------------------------------------------------------------------------- /public/images/feature-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/feature-6.png -------------------------------------------------------------------------------- /public/images/feature-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/feature-7.png -------------------------------------------------------------------------------- /public/images/feature-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/feature-8.png -------------------------------------------------------------------------------- /public/images/image-category-1-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-1-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-10-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-10-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-11-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-11-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-12-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-12-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-13-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-13-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-14-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-14-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-15-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-15-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-16-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-16-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-17-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-17-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-18-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-18-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-2-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-2-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-3-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-3-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-4-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-4-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-5-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-5-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-6-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-6-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-7-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-7-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-8-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-8-min.jpg -------------------------------------------------------------------------------- /public/images/image-category-9-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-category-9-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-1-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-1-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-10-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-10-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-11-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-11-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-12-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-12-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-13-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-13-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-14-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-14-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-15-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-15-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-2-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-2-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-3-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-3-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-4-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-4-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-5-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-5-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-6-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-6-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-7-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-7-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-8-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-8-min.jpg -------------------------------------------------------------------------------- /public/images/image-mostpicked-9-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/image-mostpicked-9-min.jpg -------------------------------------------------------------------------------- /public/images/images seeder.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/images seeder.zip -------------------------------------------------------------------------------- /public/images/img-featured-1-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/img-featured-1-min.jpg -------------------------------------------------------------------------------- /public/images/img-featured-2-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/img-featured-2-min.jpg -------------------------------------------------------------------------------- /public/images/img-featured-3-min.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/img-featured-3-min.jpg -------------------------------------------------------------------------------- /public/images/item-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-1.png -------------------------------------------------------------------------------- /public/images/item-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-2.png -------------------------------------------------------------------------------- /public/images/item-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-3.png -------------------------------------------------------------------------------- /public/images/item-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-4.png -------------------------------------------------------------------------------- /public/images/item-a-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-a-1.png -------------------------------------------------------------------------------- /public/images/item-a-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-a-2.png -------------------------------------------------------------------------------- /public/images/item-a-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-a-3.png -------------------------------------------------------------------------------- /public/images/item-a-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-a-4.png -------------------------------------------------------------------------------- /public/images/item-h-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-h-1.png -------------------------------------------------------------------------------- /public/images/item-h-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-h-2.png -------------------------------------------------------------------------------- /public/images/item-h-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/item-h-3.png -------------------------------------------------------------------------------- /public/images/logo bca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/logo bca.png -------------------------------------------------------------------------------- /public/images/logo mandiri.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/logo mandiri.png -------------------------------------------------------------------------------- /public/images/testimonial1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/testimonial1.jpg -------------------------------------------------------------------------------- /public/images/testimonial2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/public/images/testimonial2.jpg -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /routes/admin.js: -------------------------------------------------------------------------------- 1 | const router = require('express').Router(); 2 | const adminController = require('../controllers/adminController'); 3 | const { uploadSingle, uploadMultiple } = require('../middlewares/multer'); 4 | const auth = require('../middlewares/auth'); 5 | 6 | router.get('/signin', adminController.viewSignin); 7 | router.post('/signin', adminController.actionSignin); 8 | router.use(auth); 9 | router.get('/logout', adminController.actionLogout); 10 | router.get('/dashboard', adminController.viewDashboard); 11 | // endpoint category 12 | router.get('/category', adminController.viewCategory); 13 | router.post('/category', adminController.addCategory); 14 | router.put('/category', adminController.editCategory); 15 | router.delete('/category/:id', adminController.deleteCategory); 16 | // endpoint bank 17 | router.get('/bank', adminController.viewBank); 18 | router.post('/bank', uploadSingle, adminController.addBank); 19 | router.put('/bank', uploadSingle, adminController.editBank); 20 | router.delete('/bank/:id', adminController.deleteBank); 21 | // endpoint item 22 | router.get('/item', adminController.viewItem); 23 | router.post('/item', uploadMultiple, adminController.addItem); 24 | router.get('/item/show-image/:id', adminController.showImageItem); 25 | router.get('/item/:id', adminController.showEditItem); 26 | router.put('/item/:id', uploadMultiple, adminController.editItem); 27 | router.delete('/item/:id/delete', adminController.deleteItem); 28 | 29 | // endpoint detail item 30 | router.get('/item/show-detail-item/:itemId', adminController.viewDetailItem); 31 | router.post('/item/add/feature', uploadSingle, adminController.addFeature); 32 | router.put('/item/update/feature', uploadSingle, adminController.editFeature); 33 | router.delete('/item/:itemId/feature/:id', adminController.deleteFeature); 34 | 35 | router.post('/item/add/activity', uploadSingle, adminController.addActivity); 36 | router.put('/item/update/activity', uploadSingle, adminController.editActivity); 37 | router.delete('/item/:itemId/activity/:id', adminController.deleteActivity); 38 | 39 | 40 | router.get('/booking', adminController.viewBooking); 41 | router.get('/booking/:id', adminController.showDetailBooking); 42 | router.put('/booking/:id/confirmation', adminController.actionConfirmation); 43 | router.put('/booking/:id/reject', adminController.actionReject); 44 | 45 | module.exports = router; -------------------------------------------------------------------------------- /routes/api.js: -------------------------------------------------------------------------------- 1 | const router = require('express').Router(); 2 | const apiController = require('../controllers/apiController'); 3 | const { uploadSingle } = require('../middlewares/multer'); 4 | 5 | router.get('/landing-page', apiController.landingPage); 6 | router.get('/detail-page/:id', apiController.detailPage); 7 | router.post('/booking-page', uploadSingle, apiController.bookingPage); 8 | module.exports = router; 9 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function (req, res, next) { 6 | res.redirect('/admin/signin') 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /seed.js: -------------------------------------------------------------------------------- 1 | var seeder = require('mongoose-seed'); 2 | var mongoose = require('mongoose'); 3 | 4 | // Connect to MongoDB via Mongoose 5 | seeder.connect('mongodb+srv://codeathome:bwamern@cluster0-40j6e.mongodb.net/db_staycation?retryWrites=true&w=majority', { 6 | useNewUrlParser: true, 7 | useCreateIndex: true, 8 | useFindAndModify: true, 9 | useUnifiedTopology: true 10 | }, function () { 11 | 12 | // Load Mongoose models 13 | seeder.loadModels([ 14 | './models/Category', 15 | './models/Bank', 16 | './models/Item', 17 | './models/Feature', 18 | './models/Activity', 19 | './models/Member', 20 | './models/Image', 21 | './models/Member', 22 | './models/Booking', 23 | './models/Users' 24 | ]); 25 | 26 | // Clear specified collections 27 | seeder.clearModels(['Category', 'Bank', 'Item', 'Member', 'Item', 'Feature', 'Image', 'Booking', 'Users'], function () { 28 | 29 | // Callback to populate DB once collections have been cleared 30 | seeder.populateModels(data, function () { 31 | seeder.disconnect(); 32 | }); 33 | 34 | }); 35 | }); 36 | 37 | var data = [ 38 | // start category 39 | { 40 | 'model': 'Category', 41 | 'documents': [ 42 | { 43 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc901111'), 44 | name: 'Houses with beauty backyard', 45 | itemId: [ 46 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902222') }, 47 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902223') }, 48 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902224') }, 49 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902225') } 50 | ] 51 | }, 52 | { 53 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc901112'), 54 | name: 'Hotels with large living room', 55 | itemId: [ 56 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902226') }, 57 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902227') }, 58 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902228') }, 59 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902229') } 60 | ] 61 | }, 62 | { 63 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc901113'), 64 | name: 'Apartment with kitchen', 65 | itemId: [ 66 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902230') }, 67 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902231') }, 68 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902232') }, 69 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902233') } 70 | ] 71 | } 72 | ] 73 | }, 74 | // end category 75 | // start item 76 | { 77 | 'model': 'Item', 78 | 'documents': [ 79 | // Tabby Town 80 | { 81 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 82 | title: 'Tabby Town', 83 | price: 12, 84 | sumBooking: 1, 85 | country: 'Indonesia', 86 | city: 'Lampung', 87 | isPopular: false, 88 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 89 | unit: 'night', 90 | imageId: [ 91 | // done 92 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb1') }, 93 | // done 94 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb2') }, 95 | // done 96 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb3') } 97 | ], 98 | featureId: [ 99 | // done 100 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa09') }, 101 | // done 102 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa10') }, 103 | // done 104 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa11') }, 105 | // done 106 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa12') }, 107 | // done 108 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa13') }, 109 | // done 110 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa14') }, 111 | // done 112 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa15') }, 113 | // done 114 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa16') } 115 | ], 116 | activityId: [ 117 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb05') }, 118 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb06') }, 119 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb07') }, 120 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb08') } 121 | ], 122 | categoryId: '5e96cbe292b97300fc901111' 123 | }, 124 | // Seattle Rain 125 | { 126 | // done 127 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 128 | title: 'Seattle Rain', 129 | price: 20, 130 | sumBooking: 2, 131 | country: 'Indonesia', 132 | city: 'Bandung', 133 | isPopular: false, 134 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 135 | unit: 'night', 136 | imageId: [ 137 | // done 138 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb4') }, 139 | // done 140 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb5') }, 141 | // done 142 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb6') } 143 | ], 144 | featureId: [ 145 | // done 146 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 147 | // done 148 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 149 | // done 150 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 151 | // done 152 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 153 | // done 154 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 155 | // done 156 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 157 | // done 158 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 159 | // done 160 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 161 | ], 162 | activityId: [ 163 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 164 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 165 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 166 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 167 | ], 168 | categoryId: '5e96cbe292b97300fc901111' 169 | }, 170 | 171 | // Wodden Pit 172 | { 173 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902224'), 174 | title: 'Wodden Pit', 175 | price: 20, 176 | sumBooking: 3, 177 | country: 'Indonesia', 178 | city: 'Bandung', 179 | isPopular: false, 180 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 181 | unit: 'night', 182 | imageId: [ 183 | // done 184 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb7') }, 185 | // done 186 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb8') }, 187 | // done 188 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb9') } 189 | ], 190 | featureId: [ 191 | // done 192 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 193 | // done 194 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 195 | // done 196 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 197 | // done 198 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 199 | // done 200 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 201 | // done 202 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 203 | // done 204 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 205 | // done 206 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 207 | ], 208 | activityId: [ 209 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 210 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 211 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 212 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 213 | ], 214 | categoryId: '5e96cbe292b97300fc901111' 215 | }, 216 | 217 | // Anggana 218 | { 219 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902225'), 220 | title: 'Anggana', 221 | price: 20, 222 | sumBooking: 4, 223 | country: 'Indonesia', 224 | city: 'Bandung', 225 | isPopular: false, 226 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 227 | unit: 'night', 228 | imageId: [ 229 | // done 230 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd10') }, 231 | // done 232 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd11') }, 233 | // done 234 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd12') } 235 | ], 236 | featureId: [ 237 | // done 238 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 239 | // done 240 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 241 | // done 242 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 243 | // done 244 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 245 | // done 246 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 247 | // done 248 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 249 | // done 250 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 251 | // done 252 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 253 | ], 254 | activityId: [ 255 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 256 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 257 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 258 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 259 | ], 260 | categoryId: '5e96cbe292b97300fc901111' 261 | }, 262 | 263 | // Green Park 264 | { 265 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902226'), 266 | title: 'Green Park', 267 | price: 20, 268 | sumBooking: 5, 269 | country: 'Indonesia', 270 | city: 'Bandung', 271 | isPopular: false, 272 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 273 | unit: 'night', 274 | imageId: [ 275 | // done 276 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd13') }, 277 | // done 278 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd14') }, 279 | // done 280 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd15') } 281 | ], 282 | featureId: [ 283 | // done 284 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 285 | // done 286 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 287 | // done 288 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 289 | // done 290 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 291 | // done 292 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 293 | // done 294 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 295 | // done 296 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 297 | // done 298 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 299 | ], 300 | activityId: [ 301 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 302 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 303 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 304 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 305 | ], 306 | categoryId: '5e96cbe292b97300fc901112' 307 | }, 308 | 309 | // Podo Wae 310 | { 311 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902227'), 312 | title: 'Podo Wae', 313 | price: 20, 314 | sumBooking: 6, 315 | country: 'Indonesia', 316 | city: 'Bandung', 317 | isPopular: false, 318 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 319 | unit: 'night', 320 | imageId: [ 321 | // done 322 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd16') }, 323 | // done 324 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd17') }, 325 | // done 326 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd18') } 327 | ], 328 | featureId: [ 329 | // done 330 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 331 | // done 332 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 333 | // done 334 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 335 | // done 336 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 337 | // done 338 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 339 | // done 340 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 341 | // done 342 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 343 | // done 344 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 345 | ], 346 | activityId: [ 347 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 348 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 349 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 350 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 351 | ], 352 | categoryId: '5e96cbe292b97300fc901112' 353 | }, 354 | 355 | // Silver Rain 356 | { 357 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902228'), 358 | title: 'Silver Rain', 359 | price: 20, 360 | sumBooking: 7, 361 | country: 'Indonesia', 362 | city: 'Bandung', 363 | isPopular: false, 364 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 365 | unit: 'night', 366 | imageId: [ 367 | // done 368 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd19') }, 369 | // done 370 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd20') }, 371 | // done 372 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd21') } 373 | ], 374 | featureId: [ 375 | // done 376 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 377 | // done 378 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 379 | // done 380 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 381 | // done 382 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 383 | // done 384 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 385 | // done 386 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 387 | // done 388 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 389 | // done 390 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 391 | ], 392 | activityId: [ 393 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 394 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 395 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 396 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 397 | ], 398 | categoryId: '5e96cbe292b97300fc901112' 399 | }, 400 | 401 | // Cashville 402 | { 403 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902229'), 404 | title: 'Cashville', 405 | price: 20, 406 | sumBooking: 8, 407 | country: 'Indonesia', 408 | city: 'Bandung', 409 | isPopular: false, 410 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 411 | unit: 'night', 412 | imageId: [ 413 | // done 414 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd22') }, 415 | // done 416 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd23') }, 417 | // done 418 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd24') } 419 | ], 420 | featureId: [ 421 | // done 422 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 423 | // done 424 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 425 | // done 426 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 427 | // done 428 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 429 | // done 430 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 431 | // done 432 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 433 | // done 434 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 435 | // done 436 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 437 | ], 438 | activityId: [ 439 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 440 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 441 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 442 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 443 | ], 444 | categoryId: '5e96cbe292b97300fc901112' 445 | }, 446 | 447 | // PS Wood 448 | { 449 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902230'), 450 | title: 'PS Wood', 451 | price: 20, 452 | sumBooking: 9, 453 | country: 'Indonesia', 454 | city: 'Bandung', 455 | isPopular: false, 456 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 457 | unit: 'night', 458 | imageId: [ 459 | // done 460 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd25') }, 461 | // done 462 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd26') }, 463 | // done 464 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd27') } 465 | ], 466 | featureId: [ 467 | // done 468 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 469 | // done 470 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 471 | // done 472 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 473 | // done 474 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 475 | // done 476 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 477 | // done 478 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 479 | // done 480 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 481 | // done 482 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 483 | ], 484 | activityId: [ 485 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 486 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 487 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 488 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 489 | ], 490 | categoryId: '5e96cbe292b97300fc901113' 491 | }, 492 | 493 | // One Five 494 | { 495 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902231'), 496 | title: 'One Five', 497 | price: 20, 498 | sumBooking: 11, 499 | country: 'Indonesia', 500 | city: 'Bandung', 501 | isPopular: false, 502 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 503 | unit: 'night', 504 | imageId: [ 505 | // done 506 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd28') }, 507 | // done 508 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd29') }, 509 | // done 510 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd30') } 511 | ], 512 | featureId: [ 513 | // done 514 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 515 | // done 516 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 517 | // done 518 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 519 | // done 520 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 521 | // done 522 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 523 | // done 524 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 525 | // done 526 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 527 | // done 528 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 529 | ], 530 | activityId: [ 531 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 532 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 533 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 534 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 535 | ], 536 | categoryId: '5e96cbe292b97300fc901113' 537 | }, 538 | 539 | // Minimal 540 | { 541 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902232'), 542 | title: 'Minimal', 543 | price: 20, 544 | sumBooking: 13, 545 | country: 'Indonesia', 546 | city: 'Bandung', 547 | isPopular: false, 548 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 549 | unit: 'night', 550 | imageId: [ 551 | // done 552 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd32') }, 553 | // done 554 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd31') }, 555 | // done 556 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd33') } 557 | ], 558 | featureId: [ 559 | // done 560 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 561 | // done 562 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 563 | // done 564 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 565 | // done 566 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 567 | // done 568 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 569 | // done 570 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 571 | // done 572 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 573 | // done 574 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 575 | ], 576 | activityId: [ 577 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 578 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 579 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 580 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 581 | ], 582 | categoryId: '5e96cbe292b97300fc901113' 583 | }, 584 | 585 | // Stays Home 586 | { 587 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902233'), 588 | title: 'Stays Home', 589 | price: 20, 590 | sumBooking: 14, 591 | country: 'Indonesia', 592 | city: 'Bandung', 593 | isPopular: false, 594 | description: 'Minimal techno is a minimalist subgenre of techno music. It is characterized by a stripped-down aesthetic that exploits the use of repetition and understated development. Minimal techno is thought to have been originally developed in the early 1990s by Detroit-based producers Robert Hood and Daniel Bell.', 595 | unit: 'night', 596 | imageId: [ 597 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd36') }, 598 | // done 599 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd34') }, 600 | // done 601 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd35') }, 602 | // done 603 | ], 604 | featureId: [ 605 | // done 606 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01') }, 607 | // done 608 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02') }, 609 | // done 610 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03') }, 611 | // done 612 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04') }, 613 | // done 614 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05') }, 615 | // done 616 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06') }, 617 | // done 618 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07') }, 619 | // done 620 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08') } 621 | ], 622 | activityId: [ 623 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01') }, 624 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02') }, 625 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03') }, 626 | { _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04') } 627 | ], 628 | categoryId: '5e96cbe292b97300fc901113' 629 | }, 630 | 631 | ] 632 | }, 633 | // end item 634 | // start image 635 | { 636 | 'model': 'Image', 637 | 'documents': [ 638 | { 639 | // done 640 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb1'), 641 | imageUrl: 'images/image-mostpicked-1-min.jpg' 642 | }, 643 | // done 644 | { 645 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb2'), 646 | imageUrl: 'images/image-mostpicked-2-min.jpg' 647 | }, 648 | // done 649 | { 650 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb3'), 651 | imageUrl: 'images/image-mostpicked-3-min.jpg' 652 | }, 653 | { 654 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb4'), 655 | imageUrl: 'images/image-mostpicked-4-min.jpg' 656 | }, 657 | { 658 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb5'), 659 | imageUrl: 'images/item-1.png' 660 | }, 661 | { 662 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb6'), 663 | imageUrl: 'images/image-mostpicked-5-min.jpg' 664 | }, 665 | { 666 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb7'), 667 | imageUrl: 'images/image-mostpicked-7-min.jpg' 668 | }, 669 | { 670 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb8'), 671 | imageUrl: 'images/image-mostpicked-8-min.jpg' 672 | }, 673 | { 674 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cdb9'), 675 | imageUrl: 'images/image-mostpicked-9-min.jpg' 676 | }, 677 | { 678 | // done 679 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd10'), 680 | imageUrl: 'images/image-mostpicked-10-min.jpg' 681 | }, 682 | // done 683 | { 684 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd11'), 685 | imageUrl: 'images/image-mostpicked-11-min.jpg' 686 | }, 687 | // done 688 | { 689 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd12'), 690 | imageUrl: 'images/image-mostpicked-12-min.jpg' 691 | }, 692 | { 693 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd13'), 694 | imageUrl: 'images/image-mostpicked-13-min.jpg' 695 | }, 696 | { 697 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd14'), 698 | imageUrl: 'images/image-mostpicked-14-min.jpg' 699 | }, 700 | { 701 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd15'), 702 | imageUrl: 'images/image-mostpicked-15-min.jpg' 703 | }, 704 | { 705 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd16'), 706 | imageUrl: 'images/image-category-1-min.jpg' 707 | }, 708 | { 709 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd17'), 710 | imageUrl: 'images/image-category-2-min.jpg' 711 | }, 712 | { 713 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd18'), 714 | imageUrl: 'images/image-category-3-min.jpg' 715 | }, 716 | { 717 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd19'), 718 | imageUrl: 'images/image-category-4-min.jpg' 719 | }, 720 | { 721 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd20'), 722 | imageUrl: 'images/image-category-5-min.jpg' 723 | }, 724 | { 725 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd21'), 726 | imageUrl: 'images/image-category-6-min.jpg' 727 | }, 728 | { 729 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd22'), 730 | imageUrl: 'images/image-category-7-min.jpg' 731 | }, 732 | { 733 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd23'), 734 | imageUrl: 'images/image-category-8-min.jpg' 735 | }, 736 | { 737 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd24'), 738 | imageUrl: 'images/image-category-9-min.jpg' 739 | }, 740 | { 741 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd25'), 742 | imageUrl: 'images/image-category-7-min.jpg' 743 | }, 744 | { 745 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd26'), 746 | imageUrl: 'images/image-category-8-min.jpg' 747 | }, 748 | { 749 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd27'), 750 | imageUrl: 'images/image-category-9-min.jpg' 751 | }, 752 | { 753 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd28'), 754 | imageUrl: 'images/image-category-10-min.jpg' 755 | }, 756 | { 757 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd29'), 758 | imageUrl: 'images/image-category-11-min.jpg' 759 | }, 760 | { 761 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd30'), 762 | imageUrl: 'images/image-category-12-min.jpg' 763 | }, 764 | { 765 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd31'), 766 | imageUrl: 'images/image-category-13-min.jpg' 767 | }, 768 | { 769 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd32'), 770 | imageUrl: 'images/image-category-14-min.jpg' 771 | }, 772 | { 773 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd33'), 774 | imageUrl: 'images/image-category-15-min.jpg' 775 | }, 776 | { 777 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd34'), 778 | imageUrl: 'images/image-category-16-min.jpg' 779 | }, 780 | { 781 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd35'), 782 | imageUrl: 'images/image-category-17-min.jpg' 783 | }, 784 | { 785 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cd36'), 786 | imageUrl: 'images/image-category-18-min.jpg' 787 | }, 788 | ] 789 | }, 790 | // end image 791 | // start feature 792 | { 793 | 'model': 'Feature', 794 | 'documents': [ 795 | { 796 | // done 797 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa01'), 798 | name: 'bedroom', 799 | qty: 2, 800 | imageUrl: 'images/feature-1.png', 801 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 802 | }, 803 | { 804 | // done 805 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa02'), 806 | name: 'living room', 807 | qty: 23, 808 | imageUrl: 'images/feature-2.png', 809 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 810 | }, 811 | { 812 | // done 813 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa03'), 814 | name: 'televison', 815 | qty: 12, 816 | imageUrl: 'images/feature-3.png', 817 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 818 | }, 819 | { 820 | // done 821 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa04'), 822 | name: 'televison', 823 | qty: 5, 824 | imageUrl: 'images/feature-4.png', 825 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 826 | }, 827 | { 828 | // done 829 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa05'), 830 | name: 'mbp/s', 831 | qty: 5, 832 | imageUrl: 'images/feature-5.png', 833 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 834 | }, 835 | { 836 | // done 837 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa06'), 838 | name: 'unit ready', 839 | qty: 5, 840 | imageUrl: 'images/feature-6.png', 841 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 842 | }, 843 | { 844 | // done 845 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa07'), 846 | name: 'refigrator', 847 | qty: 5, 848 | imageUrl: 'images/feature-7.png', 849 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 850 | }, 851 | { 852 | // done 853 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa08'), 854 | name: 'televion', 855 | qty: 5, 856 | imageUrl: 'images/feature-8.png', 857 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 858 | }, 859 | // item 2 860 | { 861 | // done 862 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa09'), 863 | name: 'bedroom', 864 | qty: 2, 865 | imageUrl: 'images/feature-1.png', 866 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 867 | }, 868 | { 869 | // done 870 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa10'), 871 | name: 'living room', 872 | qty: 23, 873 | imageUrl: 'images/feature-2.png', 874 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 875 | }, 876 | { 877 | // done 878 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa11'), 879 | name: 'televison', 880 | qty: 12, 881 | imageUrl: 'images/feature-3.png', 882 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 883 | }, 884 | { 885 | // done 886 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa12'), 887 | name: 'televison', 888 | qty: 5, 889 | imageUrl: 'images/feature-4.png', 890 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 891 | }, 892 | { 893 | // done 894 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa13'), 895 | name: 'mbp/s', 896 | qty: 5, 897 | imageUrl: 'images/feature-5.png', 898 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 899 | }, 900 | { 901 | // done 902 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa14'), 903 | name: 'unit ready', 904 | qty: 5, 905 | imageUrl: 'images/feature-6.png', 906 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 907 | }, 908 | { 909 | // done 910 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa15'), 911 | name: 'refigrator', 912 | qty: 5, 913 | imageUrl: 'images/feature-7.png', 914 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 915 | }, 916 | { 917 | // done 918 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90aa16'), 919 | name: 'televion', 920 | qty: 5, 921 | imageUrl: 'images/feature-8.png', 922 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 923 | } 924 | ] 925 | }, 926 | // end feature 927 | // start activity 928 | { 929 | 'model': 'Activity', 930 | 'documents': [ 931 | // done 932 | { 933 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb01'), 934 | name: 'Green Lake', 935 | type: 'Nature', 936 | imageUrl: 'images/activity-1.png', 937 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 938 | }, 939 | { 940 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb02'), 941 | name: 'Dog Clubs', 942 | type: 'Pool', 943 | imageUrl: 'images/activity-2.png', 944 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 945 | }, 946 | { 947 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb03'), 948 | name: 'Labour and Wait', 949 | type: 'Shopping', 950 | imageUrl: 'images/activity-3.png', 951 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 952 | }, 953 | { 954 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb04'), 955 | name: 'Labour and Wait', 956 | type: 'Shopping', 957 | imageUrl: 'images/activity-4.png', 958 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 959 | }, 960 | // done 2 961 | { 962 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb05'), 963 | name: 'Green Lake', 964 | type: 'Nature', 965 | imageUrl: 'images/activity-3.png', 966 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 967 | }, 968 | { 969 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb06'), 970 | name: 'Dog Clubs', 971 | type: 'Pool', 972 | imageUrl: 'images/activity-2.png', 973 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 974 | }, 975 | { 976 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb07'), 977 | name: 'Labour and Wait', 978 | type: 'Shopping', 979 | imageUrl: 'images/activity-1.png', 980 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 981 | }, 982 | { 983 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90bb08'), 984 | name: 'Labour and Wait', 985 | type: 'Shopping', 986 | imageUrl: 'images/activity-4.png', 987 | itemId: mongoose.Types.ObjectId('5e96cbe292b97300fc902223'), 988 | } 989 | ] 990 | }, 991 | // end activity 992 | 993 | // start booking 994 | { 995 | 'model': 'Booking', 996 | 'documents': [ 997 | { 998 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc90cee1'), 999 | bookingStartDate: '12-12-2020', 1000 | bookingEndDate: '12-12-2020', 1001 | invoice: 1231231, 1002 | itemId: { 1003 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc902222'), 1004 | title: 'Village Angga', 1005 | price: 6, 1006 | duration: 2, 1007 | }, 1008 | total: 12, 1009 | memberId: mongoose.Types.ObjectId('5e96cbe292b97300fc903333'), 1010 | bankId: mongoose.Types.ObjectId('5e96cbe292b97300fc903323'), 1011 | payments: { 1012 | proofPayment: 'images/bukti.jpeg', 1013 | bankFrom: 'BCA', 1014 | status: 'Proses', 1015 | accountHolder: 'ang' 1016 | } 1017 | } 1018 | ] 1019 | }, 1020 | // end booking 1021 | 1022 | // member 1023 | { 1024 | 'model': 'Member', 1025 | 'documents': [ 1026 | { 1027 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc903333'), 1028 | firstName: 'Elfin', 1029 | lastName: 'Sanjaya', 1030 | email: 'elfinsanjaya12@gmail.com', 1031 | phoneNumber: '082377954008' 1032 | }, 1033 | { 1034 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc903334'), 1035 | firstName: 'Yein', 1036 | lastName: 'Narayana', 1037 | email: 'elfinsanjaya1207@gmail.com', 1038 | phoneNumber: '082377954008' 1039 | } 1040 | ] 1041 | }, 1042 | { 1043 | 'model': 'Bank', 1044 | 'documents': [ 1045 | { 1046 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc903322'), 1047 | nameBank: 'Mandiri', 1048 | nomorRekening: '089898', 1049 | name: 'elfin', 1050 | imageUrl: 'images/logo bca.png' 1051 | }, 1052 | { 1053 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc903323'), 1054 | nameBank: 'BCA', 1055 | nomorRekening: '878678', 1056 | name: 'elfin', 1057 | imageUrl: 'images/logo mandiri.png' 1058 | } 1059 | ] 1060 | }, 1061 | { 1062 | 'model': 'Users', 1063 | 'documents': [ 1064 | { 1065 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc903345'), 1066 | username: 'admin', 1067 | password: 'rahasia', 1068 | }, 1069 | { 1070 | _id: mongoose.Types.ObjectId('5e96cbe292b97300fc903346'), 1071 | username: 'super admin', 1072 | password: 'rahasia', 1073 | }, 1074 | ] 1075 | } 1076 | ]; -------------------------------------------------------------------------------- /test/buktibayar.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeathome-dev/bwa_server_staycation/7e454b6090e054b463c3fba1c87a1c91e7bcff1f/test/buktibayar.jpeg -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const chaiHttp = require('chai-http'); 3 | const expect = chai.expect; 4 | const app = require('../app'); 5 | const fs = require('fs'); 6 | 7 | chai.use(chaiHttp); 8 | 9 | describe('API ENDPOINT TESTING', () => { 10 | it('GET Landing Page', (done) => { 11 | chai.request(app).get('/api/v1/member/landing-page').end((err, res) => { 12 | expect(err).to.be.null 13 | expect(res).to.have.status(200) 14 | expect(res.body).to.be.an('object') 15 | expect(res.body).to.have.property('hero') 16 | expect(res.body.hero).to.have.all.keys('travelers', 'treasures', 'cities') 17 | expect(res.body).to.have.property('mostPicked') 18 | expect(res.body.mostPicked).to.have.an('array') 19 | expect(res.body).to.have.property('category') 20 | expect(res.body.category).to.have.an('array') 21 | expect(res.body).to.have.property('testimonial') 22 | expect(res.body.testimonial).to.have.an('object') 23 | done(); 24 | }) 25 | }) 26 | 27 | it('GET Detail Page', (done) => { 28 | chai.request(app).get('/api/v1/member/detail-page/5e96cbe292b97300fc902223').end((err, res) => { 29 | expect(err).to.be.null 30 | expect(res).to.have.status(200) 31 | expect(res.body).to.be.an('object') 32 | expect(res.body).to.have.property('country') 33 | expect(res.body).to.have.property('isPopular') 34 | expect(res.body).to.have.property('unit') 35 | expect(res.body).to.have.property('sumBooking') 36 | expect(res.body).to.have.property('imageId') 37 | expect(res.body.imageId).to.have.an('array') 38 | expect(res.body).to.have.property('featureId') 39 | expect(res.body.featureId).to.have.an('array') 40 | expect(res.body).to.have.property('activityId') 41 | expect(res.body.activityId).to.have.an('array') 42 | expect(res.body).to.have.property('_id') 43 | expect(res.body).to.have.property('title') 44 | expect(res.body).to.have.property('price') 45 | expect(res.body).to.have.property('city') 46 | expect(res.body).to.have.property('description') 47 | expect(res.body).to.have.property('__v') 48 | expect(res.body).to.have.property('bank') 49 | expect(res.body.bank).to.have.an('array') 50 | expect(res.body).to.have.property('testimonial') 51 | expect(res.body.testimonial).to.have.an('object') 52 | done(); 53 | }) 54 | }) 55 | 56 | it('POST Booking Page', (done) => { 57 | const image = __dirname + '/buktibayar.jpeg'; 58 | const dataSample = { 59 | image, 60 | idItem: '5e96cbe292b97300fc902223', 61 | duration: 2, 62 | bookingStartDate: '9-4-2020', 63 | bookingEndDate: '11-4-2020', 64 | firstName: 'itce', 65 | lastName: 'diasari', 66 | email: 'itce@gmail.com', 67 | phoneNumber: '08150008989', 68 | accountHolder: 'itce', 69 | bankFrom: 'BNI', 70 | } 71 | chai.request(app).post('/api/v1/member/booking-page') 72 | .set('Content-Type', 'application/x-www-form-urlencoded') 73 | .field('idItem', dataSample.idItem) 74 | .field('duration', dataSample.duration) 75 | .field('bookingStartDate', dataSample.bookingStartDate) 76 | .field('bookingEndDate', dataSample.bookingEndDate) 77 | .field('firstName', dataSample.firstName) 78 | .field('lastName', dataSample.lastName) 79 | .field('email', dataSample.email) 80 | .field('phoneNumber', dataSample.phoneNumber) 81 | .field('accountHolder', dataSample.accountHolder) 82 | .field('bankFrom', dataSample.bankFrom) 83 | .attach('image', fs.readFileSync(dataSample.image), 'buktibayar.jpeg') 84 | .end((err, res) => { 85 | expect(err).to.be.null 86 | expect(res).to.have.status(201) 87 | expect(res.body).to.be.an('object') 88 | expect(res.body).to.have.property('message') 89 | expect(res.body.message).to.equal('Success Booking') 90 | expect(res.body).to.have.property('booking') 91 | expect(res.body.booking).to.have.all.keys('payments', '_id', 'invoice', 'bookingStartDate', 'bookingEndDate', 'total', 'itemId', 'memberId', '__v') 92 | expect(res.body.booking.payments).to.have.all.keys('status', 'proofPayment', 'bankFrom', 'accountHolder') 93 | expect(res.body.booking.itemId).to.have.all.keys('_id', 'title', 'price', 'duration') 94 | done(); 95 | }) 96 | }) 97 | }) -------------------------------------------------------------------------------- /views/admin/bank/add_modal.ejs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /views/admin/bank/edit_modal.ejs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /views/admin/bank/table_bank.ejs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | <% if(user.role == 'admin') { %> 5 | 8 | <% } %> 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | <% if(user.role == 'admin') { %> 21 | 22 | <% } %> 23 | 24 | 25 | 26 | <% for(let i = 0; i < bank.length; i++){ %> 27 | 28 | 29 | 30 | 31 | 32 | 35 | <% if(user.role == 'admin') { %> 36 | 45 | <% } %> 46 | 47 | <% } %> 48 | 49 |
NoName BankNomor RekeningNameImageAction
<%= i + 1 %><%= bank[i].nameBank %><%= bank[i].nomorRekening %><%= bank[i].name %> 33 | imageUrl 34 | 37 |
38 | 42 | 43 |
44 |
50 |
51 |
52 |
-------------------------------------------------------------------------------- /views/admin/bank/view_bank.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../../partials/header.ejs') %> 2 | 3 | 4 |
5 | 6 | <%- include('../../partials/sidebar.ejs') %> 7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | <%- include('../../partials/navbar.ejs') %> 15 | 16 | 17 |
18 | <%- include('../../partials/message.ejs') %> 19 | 20 | 21 |

Bank

22 | 23 | 24 | <%- include('table_bank.ejs') %> 25 | 26 |
27 | 28 | 29 |
30 | 31 | 32 | <%- include('../../partials/footer.ejs') %> 33 | 34 |
35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 64 | 65 | <%- include('add_modal.ejs') %> 66 | <%- include('edit_modal.ejs') %> 67 | <%- include('../../partials/js.ejs') %> 68 | -------------------------------------------------------------------------------- /views/admin/booking/show_detail_booking.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../../partials/header.ejs') %> 2 | 3 | 4 |
5 | 6 | <%- include('../../partials/sidebar.ejs') %> 7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | <%- include('../../partials/navbar.ejs') %> 15 | 16 | 17 |
18 | 19 | <%- include('../../partials/message.ejs') %> 20 | 21 |

Detail Booking

22 | 23 | 24 | <%- include('show_table_detail_booking.ejs') %> 25 | 26 |
27 | 28 | 29 |
30 | 31 | 32 | <%- include('../../partials/footer.ejs') %> 33 | 34 |
35 | 36 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 64 | 65 | <%- include('../../partials/js.ejs') %> -------------------------------------------------------------------------------- /views/admin/booking/show_table_detail_booking.ejs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | Back 5 |
6 |
7 |
8 |
9 | image 11 | 12 |
13 | <% if(booking.payments.status == 'Proses') {%> 14 | 15 |
16 | 20 |
21 |
22 | 26 |
27 | <% } %> 28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
Name MemberTotalBank FromTitle
<%= booking.memberId.firstName %> <%= booking.memberId.lastName %>$ <%= booking.total %> <%= booking.payments.bankFrom %> <%= booking.itemId.title %>
50 |
51 |
52 |
53 |
54 |
-------------------------------------------------------------------------------- /views/admin/booking/table_booking.ejs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
DataTables Example
5 |
6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | <% for(let i = 0; i < booking.length; i++){ %> 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 35 | 36 | <% } %> 37 | 38 |
NoInvoiceDateTitleName MemberStatusAction
<%= i + 1 %>#<%= booking[i].invoice %><%= booking[i].bookingStartDate.toLocaleString().split(',')[0] %> s/d 26 | <%= booking[i].bookingEndDate.toLocaleString().split(',')[0] %> 27 | <%= booking[i].itemId.title %><%= booking[i].memberId.firstName %> <%= booking[i].memberId.lastName %><%= booking[i].payments.status %> 32 | 34 |
39 |
40 |
41 |
-------------------------------------------------------------------------------- /views/admin/booking/view_booking.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../../partials/header.ejs') %> 2 | 3 | 4 |
5 | 6 | <%- include('../../partials/sidebar.ejs') %> 7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | <%- include('../../partials/navbar.ejs') %> 15 | 16 | 17 |
18 | 19 | 20 |

Booking

21 | 22 | 23 | <%- include('table_booking.ejs') %> 24 | 25 |
26 | 27 | 28 |
29 | 30 | 31 | <%- include('../../partials/footer.ejs') %> 32 | 33 |
34 | 35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 63 | 64 | <%- include('../../partials/js.ejs') %> -------------------------------------------------------------------------------- /views/admin/category/add_modal.ejs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /views/admin/category/edit_modal.ejs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /views/admin/category/table_category.ejs: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 | <% if(user.role == 'admin') { %> 6 | 9 | <% } %> 10 |
11 |
12 |
13 | 14 | 15 | 16 | 17 | 18 | <% if(user.role == 'admin') { %> 19 | 20 | <% } %> 21 | 22 | 23 | 24 | <% for(let i = 0; i < category.length; i++){ %> 25 | 26 | 27 | 28 | <% if(user.role == 'admin') { %> 29 | 36 | <% } %> 37 | 38 | <% } %> 39 | 40 |
NoNameAction
<%= i + 1 %><%= category[i].name %> 30 |
31 | 33 | 34 |
35 |
41 |
42 |
43 |
-------------------------------------------------------------------------------- /views/admin/category/view_category.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../../partials/header.ejs') %> 2 | 3 | 4 |
5 | 6 | <%- include('../../partials/sidebar.ejs') %> 7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | <%- include('../../partials/navbar.ejs') %> 15 | 16 | 17 |
18 | <%- include('../../partials/message.ejs') %> 19 | 20 |

Category

21 | 22 | 23 | <%- include('table_category.ejs') %> 24 | 25 |
26 | 27 | 28 |
29 | 30 | 31 | <%- include('../../partials/footer.ejs') %> 32 | 33 |
34 | 35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 63 | 64 | <%- include('add_modal.ejs') %> 65 | <%- include('edit_modal.ejs') %> 66 | <%- include('../../partials/js.ejs') %> 67 | 68 | -------------------------------------------------------------------------------- /views/admin/dashboard/view_dashboard.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../../partials/header.ejs') %> 2 | 3 | 4 |
5 | 6 | <%- include('../../partials/sidebar.ejs') %> 7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | <%- include('../../partials/navbar.ejs') %> 15 | 16 | 17 |
18 | 19 | 20 |

Dashboard

21 | 22 |
23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |
Member
31 |
<%= member.length %>
32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
Order
48 |
<%= booking.length %>
49 |
50 |
51 | 52 |
53 |
54 |
55 |
56 |
57 | 58 | 59 |
60 |
61 |
62 |
63 |
64 |
Item
65 |
66 |
67 |
<%= item.length %>
68 |
69 |
70 |
71 |
72 | 73 |
74 |
75 |
76 |
77 |
78 | 79 | 80 |
81 | 82 | 83 | 84 |
85 | 86 | 87 |
88 | 89 | 90 | <%- include('../../partials/footer.ejs') %> 91 | 92 |
93 | 94 | 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 122 | 123 | <%- include('../../partials/js.ejs') %> -------------------------------------------------------------------------------- /views/admin/item/add_item.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 7 | 8 |
9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 25 |
26 |
27 | 28 | 29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |
37 |
38 |
-------------------------------------------------------------------------------- /views/admin/item/detail_item/show_activity.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
7 |
8 | 9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 22 | <% if(user.role == 'admin') { %> 23 | 24 | <% } %> 25 |
26 |
27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | <% if(user.role == 'admin') { %> 37 | 38 | <% } %> 39 | 40 | 41 | 42 | <% for(let i = 0; i < activity.length; i++){ %> 43 | 44 | 45 | 46 | 47 | 51 | <% if(user.role == 'admin') { %> 52 | 62 | <% } %> 63 | 64 | <% } %> 65 | 66 |
NoNameTypeImage URLAction
<%= i + 1 %><%= activity[i].name %><%= activity[i].type %> 48 | imageUrl 50 | 53 |
55 | 58 | 60 |
61 |
67 |
68 |
69 |
70 |
71 |
72 |
-------------------------------------------------------------------------------- /views/admin/item/detail_item/show_feature.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
7 |
8 | 9 |
10 | 11 | 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | 22 | <% if(user.role == 'admin') { %> 23 | 24 | <% } %> 25 |
26 |
27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | <% if(user.role == 'admin') { %> 37 | 38 | <% } %> 39 | 40 | 41 | 42 | 43 | <% for(let i = 0; i < feature.length; i++){ %> 44 | 45 | 46 | 47 | 48 | 52 | <% if(user.role == 'admin') { %> 53 | 62 | <% } %> 63 | 64 | <% } %> 65 | 66 | 67 |
NoNameQTYImage URLAction
<%= i + 1 %><%= feature[i].name %><%= feature[i].qty %> 49 | imageUrl 51 | 54 |
55 | 58 | 60 |
61 |
68 |
69 |
70 |
71 |
72 |
73 |
-------------------------------------------------------------------------------- /views/admin/item/detail_item/show_modal_activity.ejs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /views/admin/item/detail_item/show_modal_feature.ejs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /views/admin/item/detail_item/view_detail_item.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../../../partials/header.ejs') %> 2 | 3 | 4 |
5 | 6 | <%- include('../../../partials/sidebar.ejs') %> 7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | <%- include('../../../partials/navbar.ejs') %> 15 | 16 | 17 |
18 | <%- include('../../../partials/message.ejs') %> 19 | 20 | 21 |

Detail Item

22 | 23 | 24 | 35 |
36 | <%- include('show_feature.ejs') %> 37 | <%- include('show_activity.ejs') %> 38 |
39 | 40 | 41 | 42 |
43 | 44 | 45 |
46 | 47 | 48 | <%- include('../../../partials/footer.ejs') %> 49 | 50 |
51 | 52 | 53 |
54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 80 | <%- include('show_modal_activity.ejs') %> 81 | <%- include('show_modal_feature.ejs') %> 82 | <%- include('../../../partials/js.ejs') %> 83 | -------------------------------------------------------------------------------- /views/admin/item/edit_item.ejs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 7 | 9 |
10 |
11 | 12 | 14 |
15 |
16 | 17 | 19 |
20 |
21 | 22 | 28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 | 36 |
37 | 38 |
39 |
40 |
41 |
-------------------------------------------------------------------------------- /views/admin/item/show_image_item.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | <% for(let x = 0; x < item.imageId.length; x++){ %> 5 |
6 |
7 |
8 | image 10 |
11 |
12 |
13 | <% } %> 14 |
15 | Back 16 |
-------------------------------------------------------------------------------- /views/admin/item/show_table_item.ejs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% for(let i = 0; i < item.length; i++){ %> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 38 | 39 | <% } %> 40 | 41 |
NoTitlePriceCountryCityAction
<%= i + 1 %><%= item[i].title %>$ <%= item[i].price %><%= item[i].country %><%= item[i].city %> 26 | <% if(user.role == 'admin') { %> 27 |
28 | 30 | 32 | 33 |
34 | <% } %> 35 | 37 |
42 |
43 |
44 |
45 |
-------------------------------------------------------------------------------- /views/admin/item/view_item.ejs: -------------------------------------------------------------------------------- 1 | <%- include('../../partials/header.ejs') %> 2 | 3 | 4 |
5 | 6 | <%- include('../../partials/sidebar.ejs') %> 7 | 8 | 9 |
10 | 11 | 12 |
13 | 14 | <%- include('../../partials/navbar.ejs') %> 15 | 16 | 17 |
18 | <%- include('../../partials/message.ejs') %> 19 | 20 | 21 |

Item

22 | 23 | 24 | <% if(action == 'view') { %> 25 | 38 |
39 | <%- include('show_table_item.ejs') %> 40 | <%- include('add_item.ejs') %> 41 |
42 | 43 | <% } else if(action == 'show image') { %> 44 | 51 |
52 | <%- include('show_image_item.ejs') %> 53 |
54 | <% }else{%> 55 | 62 |
63 | <%- include('edit_item.ejs') %> 64 |
65 | <% } %> 66 | 67 | 68 |
69 | 70 | 71 |
72 | 73 | 74 | <%- include('../../partials/footer.ejs') %> 75 | 76 |
77 | 78 | 79 |
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 106 | 107 | <%- include('../../partials/js.ejs') %> 108 | -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <%= title %> 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 |
31 | <%- include('./partials/message.ejs') %> 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 |

Staycation

40 |
41 |
42 |
43 | 45 |
46 |
47 | 49 |
50 | 51 | 54 | 55 |
56 |
57 |
58 |
59 |
60 |
61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /views/partials/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /views/partials/header.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <%= title %> 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /views/partials/js.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /views/partials/message.ejs: -------------------------------------------------------------------------------- 1 | <% if(alert.message != "") { %> 2 | 5 | <% } %> -------------------------------------------------------------------------------- /views/partials/navbar.ejs: -------------------------------------------------------------------------------- 1 | 2 | 32 | -------------------------------------------------------------------------------- /views/partials/sidebar.ejs: -------------------------------------------------------------------------------- 1 | 2 | 68 | --------------------------------------------------------------------------------