Commit 22b4c811 authored by Natthaphong Malaphan's avatar Natthaphong Malaphan 😊

try again 2

parent ea3f42c4
.git/
dist/
examples/
node_modules/
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
NODE_ENV=development
SERVER_PORT=4040
JWT_SECRET=0a6b944d-d2fb-46fc-a85e-0295c986cd9f
MONGO_HOST=mongodb://localhost/odmp
MEAN_FRONTEND=angular
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/dist-server
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db
# Env file
#*.env
www.mean.io
FROM node:lts-buster
WORKDIR /usr/src/app
ADD . /usr/src/app
RUN yarn
RUN yarn build
EXPOSE 4040
CMD ["yarn", "serve"]
## Welcome to the mean stack
The mean stack is intended to provide a simple and fun starting point for cloud native fullstack javascript applications.
MEAN is a set of Open Source components that together, provide an end-to-end framework for building dynamic web applications; starting from the top (code running in the browser) to the bottom (database). The stack is made up of:
- **M**ongoDB : Document database – used by your back-end application to store its data as JSON (JavaScript Object Notation) documents
- **E**xpress (sometimes referred to as Express.js): Back-end web application framework running on top of Node.js
- **A**ngular (formerly Angular.js): Front-end web app framework; runs your JavaScript code in the user's browser, allowing your application UI to be dynamic
- **N**ode.js : JavaScript runtime environment – lets you implement your application back-end in JavaScript
### Pre-requisites
* git - [Installation guide](https://www.linode.com/docs/development/version-control/how-to-install-git-on-linux-mac-and-windows/) .
* node.js - [Download page](https://nodejs.org/en/download/) .
* npm - comes with node or download yarn - [Download page](https://yarnpkg.com/lang/en/docs/install) .
* mongodb - [Download page](https://www.mongodb.com/download-center/community) .
### Installation
```
git clone https://github.com/linnovate/mean
cd mean
cp .env.example .env
yarn
yarn start (for development)
```
### Docker based
```
git clone https://github.com/linnovate/mean
cd mean
cp .env.example .env
docker-compose up -d
```
### Credits
- The MEAN name was coined by Valeri Karpov.
- Initial concept and development was done by Amos Haviv and sponsered by Linnovate.
- Inspired by the great work of Madhusudhan Srinivasa.
# Selected Topic in Computer Science I | Group 3 # การเพิ่ม model ชื่อ Student ใน project
ชื่อเว็บ (ภาษาไทย/English) : รีวิวภาพยนตร์จ้า ( Film Review ) ## 1. เพิ่มไฟล์ `server/models/student.model.js`
## Pre-requisites ```js
git - [Installation guide](https://git-scm.com/) . const mongoose = require('mongoose');
node.js - [Download page](https://nodejs.org/en/download/) .
npm - comes with node or download yarn - [Download page](https://classic.yarnpkg.com/en/docs/install#windows-stable) .
## Installation /**
* อ่านเพิ่มเติม https://mongoosejs.com/docs/guide.html
*/
const StudentSchema = new mongoose.Schema(
{
sid: { type: String, required: true },
first: { type: String, required: true },
last: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
},
{
versionKey: false
}
);
```bash
git clone http://projectcs.sci.ubu.ac.th/Nattsumi006/topic1group3.git module.exports = mongoose.model('Student', StudentSchema);
cd mean ```
cp .env.example .env
yarn ## 2. เพิ่มไฟล์ `server/controllers/student.controller.js`
docker-compose up -d
```js
const Joi = require('joi');
const Student = require('../models/student.model');
const studentSchema = Joi.object({
sid: Joi.number().integer().required(),
first: Joi.string().required(),
last: Joi.string().required()
})
module.exports = {
insert,
get,
getAll,
search,
}
async function insert(student) {
student = await Joi.validate(student, studentSchema, { abortEarly: false });
return await new Student(student).save();
}
/**
* อ่านเพิ่มเติม https://mongoosejs.com/docs/api.html
*/
async function get(sid) {
return await Student.find({sid: sid});
}
async function getAll() {
return await Student.find();
}
async function search(key, value) {
let query = {};
query[key] = value;
return await Student.find(query);
}
```
## 3. เพิ่มไฟล์ `server/routes/student.route.js`
```js
const express = require('express');
const asyncHandler = require('express-async-handler');
const studentCtrl = require('../controllers/student.controller');
const router = express.Router();
module.exports = router;
//router.use(passport.authenticate('jwt', { session: false }))
router.route('/').post(asyncHandler(insert));
router.route('/get/:sid(\d+)').get(asyncHandler(get));
router.route('/all').get(asyncHandler(getAll));
router.route('/search').get(asyncHandler(search));
async function insert(req, res) {
let student = await studentCtrl.insert(req.body);
res.json(student);
}
async function get(req, res) {
let all_students = await studentCtrl.get(req.params['sid']);
res.json(all_students);
}
async function getAll(req, res) {
let all_students = await studentCtrl.getAll();
res.json(all_students);
}
async function search(req, res) {
let result = await studentCtrl.search(req.params['key'], req.params['value']);
res.json(result);
}
```
## 4. เพิ่มเส้นทางการเรียกในไฟล์ `server/routes/index.route.js`
```js
const express = require('express');
const userRoutes = require('./user.route');
const studentRoutes = require('./student.route');
const authRoutes = require('./auth.route');
const router = express.Router(); // eslint-disable-line new-cap
/** GET /health-check - Check service health */
router.get('/health-check', (req, res) =>
res.send('OK')
);
router.use('/auth', authRoutes);
router.use('/user', userRoutes);
router.use('/student', studentRoutes);
module.exports = router;
```
## 5. ตัวอย่างสคริปเพื่อกำหนดข้อมูลเบื้องต้น `scripts/init-students.js`
```js
const mongoose = require('mongoose');
const util = require('util');
const debug = require('debug')('express-mongoose-es6-rest-api:index');
const config = require('../server/config/config');
const Student = require('../server/models/student.model');
// connect to mongo db
const mongoUri = config.mongo.host;
mongoose.connect(mongoUri, { keepAlive: 1 });
mongoose.connection.on('error', () => {
throw new Error(`unable to connect to database: ${mongoUri}`);
});
const students = [
{ sid: 60112233440, first: 'ชูใจ', last: 'เลิศล้ำ' },
{ sid: 60112233441, first: 'มานี', last: 'รักเผ่าไทย' },
{ sid: 60112233442, first: 'ปิติ', last: 'พิทักษ์ถิ่น' },
{ sid: 60112233443, first: 'มานะ', last: 'รักเผ่าไทย' },
{ sid: 60112233444, first: 'วีระ', last: 'ประสงค์สุข' }
];
Student.insertMany(students, (error, docs) => {
if (error) {
console.error(error);
} else {
console.log(docs);
}
mongoose.connection.close();
});
```
## 6. เรียกใช้ `scripts/init-students.js`
```sh
node scripts/init-students.js
``` ```
## Details ## 7. เรียกดูข้อมูลได้จาก `http://localhost:4000/api/student/all`
เว็บไซต์ รีวิวภาพยนตร์ เป็นเว็บไซต์ที่มีการแนะนำเนื้อหาของสื่อภาพยนตร์ซึ่งมุ่งเป้าหมายที่ผู้บริโภค เขียนเชิงพรรณนา,ตัดสิน,วิเคราะห์ภาพยนตร์
ที่ผู้บริโภคมีความสนใจและเพื่อแลกเปลี่ยนความคิดเห็นเกี่ยวกับภาพยนตร์
\ No newline at end of file
theme: jekyll-theme-minimal
logo: https://www.linnovate.net/sites/all/themes/linnovate/images/mean-picture.png
<!DOCTYPE html>
<html lang="{{ site.lang | default: "en-US" }}">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% seo %}
<link rel="stylesheet" href="{{ "/assets/css/style.css?v=" | append: site.github.build_revision | relative_url }}">
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<![endif]-->
</head>
<body>
<div class="wrapper">
<header>
{% if site.logo %}
<img class="logo" src="{{site.logo | relative_url}}" alt="Logo" />
{% endif %}
<p>{{ site.description | default: site.github.project_tagline }}</p>
{% if site.github.is_project_page %}
<p class="view"><a href="{{ site.github.repository_url }}">View the Project on GitHub <small>{{ site.github.repository_nwo }}</small></a></p>
{% endif %}
<!-- Place this tag where you want the button to render. -->
<a class="github-button" href="https://github.com/linnovate/mean" data-show-count="true" aria-label="Star ntkme/github-buttons on GitHub">Star</a>
{% if site.github.is_user_page %}
<p class="view"><a href="{{ site.github.owner_url }}">View My GitHub Profile</a></p>
{% endif %}
{% if site.show_downloads %}
<ul class="downloads">
<li><a href="{{ site.github.zip_url }}">Download <strong>ZIP File</strong></a></li>
<li><a href="{{ site.github.tar_url }}">Download <strong>TAR Ball</strong></a></li>
<li><a href="{{ site.github.repository_url }}">View On <strong>GitHub</strong></a></li>
</ul>
{% endif %}
<img class="ninja" src="/assets/img/ninja.jpg"/>
</header>
<section>
{{ content }}
{% if site.github.is_project_page %}
<p>This project is maintained by <a href="{{ site.github.owner_url }}">{{ site.github.owner_name }}</a></p>
{% endif %}
</section>
</div>
<script src="{{ "/assets/js/scale.fix.js" | relative_url }}"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-36499287-4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
// gtag('config', 'UA-XXXXXX-XX'); // change this to your own UA config
</script>
<!-- Place this tag in your head or just before your close body tag. -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"mean": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "./tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"assets": [
"src/assets",
"src/favicon.ico"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "mean:build"
},
"configurations": {
"production": {
"browserTarget": "mean:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "mean:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "./tsconfig.spec.json",
"scripts": [],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
"src/styles.scss"
],
"assets": [
"src/assets",
"src/favicon.ico"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"./tsconfig.app.json",
"./tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"deploy": {
"builder": "angular-cli-ghpages:deploy",
"options": {}
}
}
}
},
"defaultProject": "mean",
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"styleext": "scss"
},
"@schematics/angular:directive": {
"prefix": "app"
}
},
"cli": {
"analytics": "c4b1fc92-ebd6-4fa9-a8a1-067afbef23d9"
}
}
\ No newline at end of file
---
---
@import "{{ site.theme }}";
h1 {
a {
color:#00758f;
text-size:40px;
}
}
header {
img.logo {
margin-left:30px;
display:block;
height: auto;
width: auto;
max-width: 150px;
max-height: 200px;
margin-bottom: 17%;
}
img.ninja {
margin-top: 20px;
height: auto;
width: auto;
max-width: 350px;
max-height: 200px;
margin-bottom: 50px;
}
}
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11
version: '3'
services:
app:
build: ./
image: mean
container_name: mean
ports:
- 80:4040
expose:
- 4040
environment:
NODE_ENV: production
SERVER_PORT: 4040
JWT_SECRET: 0a6b944d-d2fb-46fc-a85e-0295c986cd9f
MONGO_HOST: mongodb://mongo/odmp
restart: always
depends_on:
- mongo
meanexpress:
container_name: meanexpress
image: mongo-express
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_AUTH_DATABASE: odmp
#ME_CONFIG_MONGODB_AUTH_USERNAME: admin
#ME_CONFIG_MONGODB_AUTH_PASSWORD: pass
restart: always
depends_on:
- mongo
mongo:
container_name: meanmongo
image: mongo:3.6
ports:
- 27017:27017
expose:
- 27017
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client:{
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
angularCli: {
environment: 'dev'
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
{
"name": "mean",
"version": "2.0.2",
"license": "MIT",
"scripts": {
"ng": "ng",
"serve": "node server",
"import student": "node scripts/init-students.js",
"start": "concurrently -c \"yellow.bold,green.bold\" -n \"SERVER,BUILD\" \"nodemon server\" \"ng build --watch\"",
"build": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"postinstall": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points"
},
"private": true,
"dependencies": {
"@angular/animations": "^9.1.4",
"@angular/cdk": "^9.2.1",
"@angular/common": "^9.1.4",
"@angular/compiler": "^9.1.4",
"@angular/core": "^9.1.4",
"@angular/forms": "^9.1.4",
"@angular/material": "^9.2.1",
"@angular/platform-browser": "^9.1.4",
"@angular/platform-browser-dynamic": "^9.1.4",
"@angular/router": "^9.1.4",
"bcrypt": "^3.0.2",
"body-parser": "^1.18.2",
"compression": "^1.7.2",
"cookie-parser": "^1.4.3",
"cors": "^2.8.4",
"dotenv": "^6.0.0",
"events": "^3.0.0",
"express": "^4.16.3",
"express-async-handler": "^1.1.3",
"express-jwt": "^5.3.1",
"express-validation": "^1.0.2",
"formidable": "^1.2.1",
"helmet": "^3.21.1",
"http-errors": "^1.6.3",
"joi": "^13.3.0",
"jsonwebtoken": "^8.2.1",
"method-override": "^2.3.10",
"mongoose": "^5.7.5",
"morgan": "^1.9.1",
"nodemon": "^1.17.5",
"passport": "^0.4.0",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"rxjs": "^6.5.5",
"swagger-ui-express": "^3.0.9",
"zone.js": "~0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.901.4",
"@angular/cli": "^9.1.4",
"@angular/compiler-cli": "^9.1.4",
"@angular/language-service": "^9.1.4",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~12.12.14",
"angular-cli-ghpages": "^0.6.2",
"codelyzer": "^5.2.0",
"concurrently": "^3.5.1",
"jasmine-core": "~3.1.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "^3.1.3",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "2.0.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "1.1.0",
"ts-node": "~6.1.0",
"tslint": "^5.20.1",
"typescript": "3.8.3"
}
}
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
const mongoose = require('mongoose');
const util = require('util');
const debug = require('debug')('express-mongoose-es6-rest-api:index');
const config = require('../server/config/config');
const Student = require('../server/models/student.model');
// connect to mongo db
const mongoUri = config.mongo.host;
mongoose.connect(mongoUri, { keepAlive: 1 });
mongoose.connection.on('error', () => {
throw new Error(`unable to connect to database: ${mongoUri}`);
});
const students = [
{ sid: 60112233440, first: 'ชูใจ', last: 'เลิศล้ำ' },
{ sid: 60112233441, first: 'มานี', last: 'รักเผ่าไทย' },
{ sid: 60112233442, first: 'ปิติ', last: 'พิทักษ์ถิ่น' },
{ sid: 60112233443, first: 'มานะ', last: 'รักเผ่าไทย' },
{ sid: 60112233444, first: 'วีระ', last: 'ประสงค์สุข' }
];
Student.insertMany(students, (error, docs) => {
if (error) {
console.error(error);
} else {
console.log(docs);
}
mongoose.connection.close();
});
const Joi = require('joi');
// require and configure dotenv, will load vars in .env in PROCESS.ENV
require('dotenv').config();
// define validation for all the env vars
const envVarsSchema = Joi.object({
NODE_ENV: Joi.string()
.allow(['development', 'production', 'test', 'provision'])
.default('development'),
SERVER_PORT: Joi.number()
.default(4040),
MONGOOSE_DEBUG: Joi.boolean()
.when('NODE_ENV', {
is: Joi.string().equal('development'),
then: Joi.boolean().default(true),
otherwise: Joi.boolean().default(false)
}),
JWT_SECRET: Joi.string().required()
.description('JWT Secret required to sign'),
MONGO_HOST: Joi.string().required()
.description('Mongo DB host url'),
MONGO_PORT: Joi.number()
.default(27017)
}).unknown()
.required();
const { error, value: envVars } = Joi.validate(process.env, envVarsSchema);
if (error) {
throw new Error(`Config validation error: ${error.message}`);
}
const config = {
env: envVars.NODE_ENV,
port: envVars.SERVER_PORT,
mongooseDebug: envVars.MONGOOSE_DEBUG,
jwtSecret: envVars.JWT_SECRET,
frontend: envVars.MEAN_FRONTEND || 'angular',
mongo: {
host: envVars.MONGO_HOST,
port: envVars.MONGO_PORT
}
};
module.exports = config;
const path = require('path');
const express = require('express');
const httpError = require('http-errors');
const logger = require('morgan');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const compress = require('compression');
const methodOverride = require('method-override');
const cors = require('cors');
const helmet = require('helmet');
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./swagger.json');
const routes = require('../routes/index.route');
const config = require('./config');
const passport = require('./passport')
const app = express();
if (config.env === 'development') {
app.use(logger('dev'));
}
// Choose what fronten framework to serve the dist from
var distDir = '../../dist/';
if (config.frontend == 'react'){
distDir ='../../node_modules/material-dashboard-react/dist'
}else{
distDir ='../../dist/' ;
}
//
app.use(express.static(path.join(__dirname, distDir)))
app.use(/^((?!(api)).)*/, (req, res) => {
res.sendFile(path.join(__dirname, distDir + '/index.html'));
});
console.log(distDir);
//React server
app.use(express.static(path.join(__dirname, '../../node_modules/material-dashboard-react/dist')))
app.use(/^((?!(api)).)*/, (req, res) => {
res.sendFile(path.join(__dirname, '../../dist/index.html'));
});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(compress());
app.use(methodOverride());
// secure apps by setting various HTTP headers
app.use(helmet());
// enable CORS - Cross Origin Resource Sharing
app.use(cors());
app.use(passport.initialize());
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
// API router
app.use('/api/', routes);
// catch 404 and forward to error handler
app.use((req, res, next) => {
const err = new httpError(404)
return next(err);
});
// error handler, send stacktrace only during development
app.use((err, req, res, next) => {
// customize Joi validation errors
if (err.isJoi) {
err.message = err.details.map(e => e.message).join("; ");
err.status = 400;
}
res.status(err.status || 500).json({
message: err.message
});
next(err);
});
module.exports = app;
const mongoose = require('mongoose');
const util = require('util');
const debug = require('debug')('express-mongoose-es6-rest-api:index');
const config = require('./config');
// connect to mongo db
const mongoUri = config.mongo.host;
mongoose.connect(mongoUri, { keepAlive: 1 });
mongoose.connection.on('error', () => {
throw new Error(`unable to connect to database: ${mongoUri}`);
});
// print mongoose logs in dev env
if (config.MONGOOSE_DEBUG) {
mongoose.set('debug', (collectionName, method, query, doc) => {
debug(`${collectionName}.${method}`, util.inspect(query, false, 20), doc);
});
}
const passport = require('passport');
const LocalStrategy = require('passport-local');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const bcrypt = require('bcrypt');
const User = require('../models/user.model');
const config = require('./config');
const localLogin = new LocalStrategy({
usernameField: 'email'
}, async (email, password, done) => {
let user = await User.findOne({ email });
if (!user || !bcrypt.compareSync(password, user.hashedPassword)) {
return done(null, false, { error: 'Your login details could not be verified. Please try again.' });
}
user = user.toObject();
delete user.hashedPassword;
done(null, user);
});
const jwtLogin = new JwtStrategy({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: config.jwtSecret
}, async (payload, done) => {
let user = await User.findById(payload._id);
if (!user) {
return done(null, false);
}
user = user.toObject();
delete user.hashedPassword;
done(null, user);
});
passport.use(jwtLogin);
passport.use(localLogin);
module.exports = passport;
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Mean Application API",
"description": "Mean Application API",
"license": {
"name": "MIT",
"url": "https://opensource.org/licenses/MIT"
}
},
"host": "localhost:4040",
"basePath": "/api/",
"tags": [
{
"name": "Users",
"description": "API for users in the system"
},
{
"name": "Auth",
"description": "API for auth in the system"
}
],
"schemes": [
"http"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"securityDefinitions": {
"AuthHeader": {
"type": "apiKey",
"in": "header",
"name": "Authorization"
}
},
"paths": {
"/auth/login": {
"post": {
"tags": ["Auth"],
"description": "Login to the system",
"parameters": [{
"name": "auth",
"in": "body",
"description": "User auth details",
"schema": {
"type": "object",
"required": ["email", "password"],
"properties": {
"email": {
"type": "string"
},
"password": {
"type": "string"
}
}
}
}],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "User is loggedin",
"schema": {
"$ref": "#/definitions/User"
}
}
}
}
}
},
"definitions": {
"User": {
"required": [
"email",
"fullname"
],
"properties": {
"_id": {
"type": "string",
"uniqueItems": true
},
"email": {
"type": "string",
"uniqueItems": true
},
"fullname": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"roles": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"Users": {
"type": "array",
"$ref": "#/definitions/User"
},
"Auth": {
"type": "object",
"properties": [{
"token": {
"type": "string"
},
"user": {
"$ref": "#/definitions/User"
}
}]
}
}
}
\ No newline at end of file
const jwt = require('jsonwebtoken');
const config = require('../config/config');
module.exports = {
generateToken
}
function generateToken(user) {
const payload = JSON.stringify(user);
return jwt.sign(payload, config.jwtSecret);
}
const Joi = require("joi");
const Review = require("../models/review.model");
const reviewSchema = Joi.object({
// sid: Joi.number().integer().required(),
user_id: Joi.string().required(),
user_name: Joi.string().required(),
catagory: Joi.string().required(),
namemovie: Joi.string().required(),
title: Joi.string().required(),
description: Joi.string().required(),
});
module.exports = {
insert,
get,
getAll,
search,
deleteData,
updateData,
};
async function insert(review) {
review = await Joi.validate(review, reviewSchema, { abortEarly: false });
return await new Review(review).save();
}
/**
* อ่านเพิ่มเติม https://mongoosejs.com/docs/api.html
*/
async function get(_id) {
return await Review.find({ _id: _id });
}
async function getAll() {
return await Review.find();
}
async function search(key, value) {
let query = {};
query[key] = value;
return await Review.find(query);
}
async function deleteData(_id) {
return await Review.findByIdAndDelete(_id);
}
async function updateData(_id,data) {
return Review.findByIdAndUpdate(_id, data);
}
const bcrypt = require('bcrypt');
const Joi = require('joi');
const User = require('../models/user.model');
const userSchema = Joi.object({
fullname: Joi.string().required(),
email: Joi.string().email(),
mobileNumber: Joi.string().regex(/^[1-9][0-9]{9}$/),
password: Joi.string().required(),
repeatPassword: Joi.string().required().valid(Joi.ref('password'))
})
module.exports = {
insert
}
async function insert(user) {
user = await Joi.validate(user, userSchema, { abortEarly: false });
user.hashedPassword = bcrypt.hashSync(user.password, 10);
delete user.password;
return await new User(user).save();
}
// config should be imported before importing any other file
const config = require('./config/config');
const app = require('./config/express');
require('./config/mongoose');
// module.parent check is required to support mocha watch
// src: https://github.com/mochajs/mocha/issues/1912
if (!module.parent) {
app.listen(config.port, () => {
console.info(`server started on port ${config.port} (${config.env})`);
});
}
module.exports = app;
const httpError = require('http-errors');
const requireAdmin = function (req, res, next) {
if (req.user && req.user.roles.indexOf('admin') > -1)
return next();
const err = new httpError(401);
return next(err);
}
module.exports = requireAdmin;
const mongoose = require("mongoose");
/**
* อ่านเพิ่มเติม https://mongoosejs.com/docs/guide.html
*/
const ReviewSchema = new mongoose.Schema(
{
user_id: { type: String, required: true },
user_name: { type: String, required: true },
catagory: { type: String, required: true },
namemovie: { type: String, required: true },
title: { type: String, required: true },
description: { type: String, required: true },
createdAt: { type: Date, default: Date.now },
},
{
versionKey: false,
}
);
module.exports = mongoose.model("Review", ReviewSchema);
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
fullname: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true,
// Regexp to validate emails with more strict rules as added in tests/users.js which also conforms mostly with RFC2822 guide lines
match: [/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 'Please enter a valid email'],
},
hashedPassword: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
},
roles: [{
type: String,
}]
}, {
versionKey: false
});
module.exports = mongoose.model('User', UserSchema);
const express = require('express');
const asyncHandler = require('express-async-handler')
const passport = require('passport');
const userCtrl = require('../controllers/user.controller');
const authCtrl = require('../controllers/auth.controller');
const config = require('../config/config');
const router = express.Router();
module.exports = router;
router.post('/register', asyncHandler(register), login);
router.post('/login', passport.authenticate('local', { session: false }), login);
router.get('/me', passport.authenticate('jwt', { session: false }), login);
async function register(req, res, next) {
let user = await userCtrl.insert(req.body);
user = user.toObject();
delete user.hashedPassword;
req.user = user;
next()
}
function login(req, res) {
let user = req.user;
let token = authCtrl.generateToken(user);
res.json({ user, token });
}
const express = require('express');
const userRoutes = require('./user.route');
const reviewRoutes = require('./review.route');
const authRoutes = require('./auth.route');
const router = express.Router(); // eslint-disable-line new-cap
/** GET /health-check - Check service health */
router.get('/health-check', (req, res) =>
res.send('OK')
);
router.use('/auth', authRoutes);
router.use('/user', userRoutes);
router.use('/review', reviewRoutes);
module.exports = router;
const express = require('express');
const asyncHandler = require('express-async-handler');
const reviewCtrl = require('../controllers/review.controller');
const router = express.Router();
module.exports = router;
//router.use(passport.authenticate('jwt', { session: false }))
router.route('/').post(asyncHandler(insert));
router.route('/get/:_id').get(asyncHandler(get));
router.route('/all').get(asyncHandler(getAll));
router.route('/search').get(asyncHandler(search));
router.route('/delete/:_id').delete(asyncHandler(deleteData));
router.route('/update/:_id').put(asyncHandler(updateData));
async function insert(req, res) {
let review = await reviewCtrl.insert(req.body);
res.json(review);
}
async function get(req, res) {
let all_reviews = await reviewCtrl.get(req.params['_id']);
res.json(all_reviews);
}
async function getAll(req, res) {
let all_reviews = await reviewCtrl.getAll();
res.json(all_reviews);
}
async function search(req, res) {
let result = await reviewCtrl.search(req.params['key'], req.params['value']);
res.json(result);
}
async function deleteData(req, res) {
let all_reviews = await reviewCtrl.deleteData(req.params['_id']);
res.json(all_reviews);
}
async function updateData(req, res) {
let all_reviews = await reviewCtrl.updateData(req.params['_id'],req.body);
res.json(all_reviews);
}
const express = require('express');
const passport = require('passport');
const asyncHandler = require('express-async-handler');
const userCtrl = require('../controllers/user.controller');
const router = express.Router();
module.exports = router;
router.use(passport.authenticate('jwt', { session: false }))
router.route('/')
.post(asyncHandler(insert));
async function insert(req, res) {
let user = await userCtrl.insert(req.body);
res.json(user);
}
/*-----------------------------------------------
Variables
-----------------------------------------------*/
$linesColor: #dbdbdb;
$categoryTitleColor: #686868;
$categoryEntityColor: #3F3F3F;
\ No newline at end of file
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './admin.component';
import { OnlyAdminUsersGuard } from './admin-user-guard';
const routes: Routes = [{
path: 'admin',
canActivate: [OnlyAdminUsersGuard],
children: [{
path: '',
component: AdminComponent,
}]
}];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingModule {}
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AuthService } from '@app/shared/services';
@Injectable()
export class OnlyAdminUsersGuard implements CanActivate {
constructor(private authService: AuthService) {}
canActivate(): Observable<boolean> {
return this.authService.getUser().pipe(map(user => !!user?.isAdmin));
}
}
<h4>HELLO FROM ADMIN PAGE</h4>
\ No newline at end of file
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
})
export class AdminComponent implements OnInit {
constructor() {}
public ngOnInit() {
}
}
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import { AdminRoutingModule } from './admin-routing.module';
import {AdminComponent} from './admin.component';
import {OnlyAdminUsersGuard} from './admin-user-guard';
@NgModule({
declarations: [
AdminComponent
],
imports: [
CommonModule,
AdminRoutingModule,
],
providers: [
OnlyAdminUsersGuard
]})
export class AdminModule {}
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from './shared/guards';
import { HomeComponent } from './home/home.component';
import { HeroComponent } from './hero/hero.component';
import { CreateComponent } from './create/create.component';
import { ViewdataComponent } from './viewdata/viewdata.component';
import { EditComponent } from './edit/edit.component';
const routes: Routes = [
{
path: '',
component: HomeComponent,
canActivate: [AuthGuard],
},
{
path: 'create',
component: CreateComponent,
canActivate: [AuthGuard],
},
{
path: 'viewdata/:dataId',
component: ViewdataComponent,
canActivate: [AuthGuard],
},
{
path: 'edit/:dataId',
component: EditComponent,
canActivate: [AuthGuard],
},
{
path: 'hero',
component: HeroComponent,
},
{
path: 'auth',
loadChildren: () => import('./auth/auth.module').then(m => m.AuthModule),
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
<app-header [user]="user$ | async"></app-header>
<div class="wrapper-app">
<router-outlet></router-outlet>
</div>
<footer></footer>
.wrapper-app {
}
\ No newline at end of file
import { Component } from '@angular/core';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { merge, Observable } from 'rxjs';
import { User } from './shared/interfaces';
import { AuthService } from './shared/services';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
user$: Observable<User | null> = merge(
// Init on startup
this.authService.me(),
// Update after login/register/logout
this.authService.getUser()
);
constructor(
private domSanitizer: DomSanitizer,
private matIconRegistry: MatIconRegistry,
private authService: AuthService
) {
this.registerSvgIcons();
}
registerSvgIcons() {
[
'close',
'add',
'add-blue',
'airplane-front-view',
'air-station',
'balloon',
'boat',
'cargo-ship',
'car',
'catamaran',
'clone',
'convertible',
'delete',
'drone',
'fighter-plane',
'fire-truck',
'horseback-riding',
'motorcycle',
'railcar',
'railroad-train',
'rocket-boot',
'sailing-boat',
'segway',
'shuttle',
'space-shuttle',
'steam-engine',
'suv',
'tour-bus',
'tow-truck',
'transportation',
'trolleybus',
'water-transportation',
].forEach(icon => {
this.matIconRegistry.addSvgIcon(
icon,
this.domSanitizer.bypassSecurityTrustResourceUrl(`assets/icons/${icon}.svg`)
);
});
}
}
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { MatGridListModule } from '@angular/material/grid-list';
import { SharedModule } from './shared/shared.module';
import { AppComponent } from './app.component';
import { AuthHeaderInterceptor } from './interceptors/header.interceptor';
import { CatchErrorInterceptor } from './interceptors/http-error.interceptor';
import { AppRoutingModule } from './app-routing.module';
import { HeaderComponent } from './header/header.component';
import { HomeComponent } from './home/home.component';
import { AuthService } from './shared/services';
import { HeroComponent } from './hero/hero.component';
import { CreateComponent } from './create/create.component';
import { ViewdataComponent } from './viewdata/viewdata.component';
import { EditComponent } from './edit/edit.component';
import { MatTableModule } from '@angular/material/table';
export function appInitializerFactory(authService: AuthService) {
return () => authService.checkTheUserOnTheFirstLoad();
}
@NgModule({
imports: [BrowserAnimationsModule, HttpClientModule, SharedModule, AppRoutingModule, MatGridListModule, MatTableModule],
declarations: [AppComponent, HeaderComponent, HomeComponent, HeroComponent, CreateComponent, ViewdataComponent, EditComponent],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthHeaderInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: CatchErrorInterceptor,
multi: true,
},
{
provide: APP_INITIALIZER,
useFactory: appInitializerFactory,
multi: true,
deps: [AuthService],
},
],
bootstrap: [AppComponent],
})
export class AppModule { }
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
const routes: Routes = [
{
path: '',
children: [
{
path: '',
redirectTo: '/auth/login',
pathMatch: 'full',
},
{
path: 'login',
component: LoginComponent,
},
{
path: 'register',
component: RegisterComponent,
},
],
},
];
export const AuthRoutingModule = RouterModule.forChild(routes);
.example-icon {
padding: 0 14px;
}
.example-spacer {
flex: 1 1 auto;
}
.example-card {
width: 400px;
margin: 10% auto;
}
.mat-card-title {
font-size: 16px;
}
import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { AuthRoutingModule } from './auth-routing.module';
@NgModule({
imports: [SharedModule, AuthRoutingModule],
declarations: [LoginComponent, RegisterComponent],
})
export class AuthModule {}
<mat-card class="example-card">
<mat-card-header>
<mat-card-title>Login</mat-card-title>
</mat-card-header>
<mat-card-content>
<form class="example-form">
<table cellspacing="0">
<tr>
<td>
<mat-form-field>
<input matInput placeholder="Email" [(ngModel)]="email" name="email" required>
</mat-form-field>
</td>
</tr>
<tr>
<td>
<mat-form-field>
<input matInput placeholder="Password" [(ngModel)]="password" type="password" name="password" required>
</mat-form-field>
</td>
</tr>
</table>
</form>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button (click)="login()" color="primary">Login</button>
<span>Don't have an account ? <a [routerLink]="['/auth/register']" >register</a> here</span>
</mat-card-actions>
</mat-card>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '@app/shared/services';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['../auth.component.scss'],
})
export class LoginComponent {
email: string | null = null;
password: string | null = null;
constructor(private router: Router, private authService: AuthService) {}
login(): void {
this.authService.login(this.email!, this.password!).subscribe(() => {
this.router.navigateByUrl('/');
});
}
}
<mat-card class="example-card">
<mat-card-header>
<mat-card-title>Register</mat-card-title>
</mat-card-header>
<mat-card-content>
<form class="example-form">
<table cellspacing="0" [formGroup]="userForm">
<tr>
<td>
<mat-form-field>
<input matInput placeholder="Fullname" formControlName="fullname" name="fullname" required>
</mat-form-field>
</td>
</tr>
<tr>
<td>
<mat-form-field>
<input matInput placeholder="Email" formControlName="email" name="email" required>
<mat-error *ngIf="email.invalid && email.hasError('email')">Invalid email address</mat-error>
</mat-form-field>
</td>
</tr>
<tr>
<td>
<mat-form-field>
<input matInput placeholder="Password" formControlName="password" type="password" name="password" required>
</mat-form-field>
</td>
</tr>
<tr>
<td>
<mat-form-field>
<input matInput placeholder="Repeat Password" formControlName="repeatPassword" type="password" name="repeatPassword" required>
<mat-error *ngIf="repeatPassword.invalid && repeatPassword.hasError('passwordMatch')">Password mismatch</mat-error>
</mat-form-field>
</td>
</tr>
</table>
</form>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button (click)="register()" color="primary">Register</button>
<span>Allrady have an account ? <a [routerLink]="['/auth/login']">login</a> here</span>
</mat-card-actions>
</mat-card>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RegisterComponent } from './register.component';
describe('RegisterComponent', () => {
let component: RegisterComponent;
let fixture: ComponentFixture<RegisterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RegisterComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RegisterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import {
FormGroup,
FormControl,
Validators,
ValidationErrors,
AbstractControl,
} from '@angular/forms';
import { AuthService } from '@app/shared/services';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['../auth.component.scss'],
})
export class RegisterComponent {
constructor(private router: Router, private authService: AuthService) {}
passwordsMatchValidator(control: FormControl): ValidationErrors | null {
const password = control.root.get('password');
return password && control.value !== password.value
? {
passwordMatch: true,
}
: null;
}
userForm = new FormGroup({
fullname: new FormControl('', [Validators.required]),
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required]),
repeatPassword: new FormControl('', [Validators.required, this.passwordsMatchValidator]),
});
get fullname(): AbstractControl {
return this.userForm.get('fullname')!;
}
get email(): AbstractControl {
return this.userForm.get('email')!;
}
get password(): AbstractControl {
return this.userForm.get('password')!;
}
get repeatPassword(): AbstractControl {
return this.userForm.get('repeatPassword')!;
}
register(): void {
if (this.userForm.invalid) {
return;
}
const { fullname, email, password, repeatPassword } = this.userForm.getRawValue();
this.authService.register(fullname, email, password, repeatPassword).subscribe(data => {
this.router.navigate(['']);
});
}
}
.example-icon {
padding: 0 14px;
}
.example-spacer {
flex: 1 1 auto;
}
.example-card {
width: 800px;
margin: auto;
margin-top: 4%;
}
\ No newline at end of file
<mat-card class="example-card">
<mat-card-header>
<h3>บันทึกข้อมูล</h3>
</mat-card-header>
<form>
<div class="example-container">
<p style="text-align: center">
<mat-form-field appearance="outline" style="width: 400px">
<mat-label>หมวดหมู่หนัง</mat-label>
<br />
<mat-select [(ngModel)]="reviewAdd.catagory" name="catagory">
<mat-option
value="option"
*ngFor="let catagory of catagorys"
[value]="catagory.type"
>{{ catagory.type }}</mat-option
>
</mat-select>
</mat-form-field>
<br />
<mat-form-field appearance="outline" style="width: 400px">
<mat-label>ชื่อหนัง</mat-label>
<input
matInput
placeholder="Placeholder"
name="namemovie"
[(ngModel)]="reviewAdd.namemovie"
/>
</mat-form-field>
<br />
<mat-form-field appearance="outline" style="width: 400px">
<mat-label>หัวข้อ</mat-label>
<input
matInput
placeholder="Placeholder"
name="title"
[(ngModel)]="reviewAdd.title"
/>
</mat-form-field>
<br />
<mat-form-field appearance="outline" style="width: 400px">
<mat-label>คำอธิบาย</mat-label>
<textarea
rows="10"
matInput
placeholder="Placeholder"
name="description"
[(ngModel)]="reviewAdd.description"
></textarea>
</mat-form-field>
</p>
</div>
<p style="text-align: center">
<button
style="width: 200px; margin-right: 5%"
mat-raised-button
(click)="submitReviewAdd()"
color="primary"
*ngIf="
reviewAdd.catagory &&
reviewAdd.namemovie &&
reviewAdd.title &&
reviewAdd.description
"
>
บันทึก
</button>
<button
style="width: 100px; background-color: #d32f2f; color: white"
mat-raised-button
routerLink="/"
>
ยกเลิก
</button>
</p>
</form>
</mat-card>
<br />
<br />
<br />
<br />
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateComponent } from './create.component';
describe('CreateComponent', () => {
let component: CreateComponent;
let fixture: ComponentFixture<CreateComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CreateComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CreateComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit , Input} from '@angular/core';
import { catagory } from '../../catagory';
import { CreateService } from '../service/create.service';
import { AuthService } from '@app/shared/services';
@Component({
selector: 'app-create',
templateUrl: './create.component.html',
styleUrls: ['./create.component.css']
})
export class CreateComponent implements OnInit {
user_data: any;
reviewAdd = {
'user_id': '',
'user_name': '',
'catagory': '',
'namemovie': '',
'title': '',
'description': ''
};
catagorys = catagory;
constructor(private createService: CreateService, private authService: AuthService) { }
ngOnInit(): void {
this.authService.getUser().subscribe(data => this.user_data = data);
this.reviewAdd = {
'user_id': this.user_data._id,
'user_name': this.user_data.fullname,
'catagory': '',
'namemovie': '',
'title': '',
'description': ''
};
}
submitReviewAdd() {
this.createService.postReview(this.reviewAdd).subscribe((response: {}) =>
alert('บันทึกเรียบร้อย'),
);
}
}
.example-icon {
padding: 0 14px;
}
.example-spacer {
flex: 1 1 auto;
}
.example-card {
width: 800px;
margin: auto;
margin-top: 4%;
}
<mat-card class="example-card">
<mat-card-header>
<h3>แก้ไขข้อมูล</h3>
</mat-card-header>
<form>
<div class="example-container">
<p style="text-align: center">
<mat-form-field appearance="outline" style="width: 400px">
<mat-label>หมวดหมู่หนัง</mat-label>
<mat-select [(ngModel)]="data.catagory" name="catagory">
<mat-option
value="option"
*ngFor="let catagory of catagorys"
[value]="catagory.type"
>{{ catagory.type }}</mat-option
>
</mat-select>
</mat-form-field>
<br />
<mat-form-field appearance="outline" style="width: 400px">
<mat-label>ชื่อหนัง</mat-label>
<input
matInput
placeholder="Placeholder"
name="namemovie"
[(ngModel)]="data.namemovie"
/>
</mat-form-field>
<br />
<mat-form-field appearance="outline" style="width: 400px">
<mat-label>หัวข้อ</mat-label>
<input
matInput
placeholder="Placeholder"
name="title"
[(ngModel)]="data.title"
/>
</mat-form-field>
<br />
<mat-form-field appearance="outline" style="width: 400px">
<mat-label>คำอธิบาย</mat-label>
<textarea
rows="10"
matInput
placeholder="Placeholder"
name="description"
[(ngModel)]="data.description"
></textarea>
</mat-form-field>
</p>
<p style="text-align: center">
<button
style="width: 200px; margin-right: 5%"
mat-raised-button
(click)="submitUpdate()"
color="primary"
*ngIf="
data.catagory && data.namemovie && data.title && data.description
"
>
บันทึก
</button>
<button
style="width: 100px; background-color: #d32f2f; color: white"
mat-raised-button
[routerLink]="['/viewdata', data._id]"
>
ยกเลิก
</button>
</p>
</div>
</form>
</mat-card>
<br />
<br />
<br />
<br />
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EditComponent } from './edit.component';
describe('EditComponent', () => {
let component: EditComponent;
let fixture: ComponentFixture<EditComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ EditComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { catagory } from '../../catagory';
import { EditService } from '../service/edit.service';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-edit',
templateUrl: './edit.component.html',
styleUrls: ['./edit.component.css']
})
export class EditComponent implements OnInit {
catagorys = catagory;
dataId: any;
data: any;
constructor(private route: ActivatedRoute, private editService: EditService) { }
ngOnInit(): void {
this.route.paramMap.subscribe(params => {
this.dataId = params.get('dataId');
});
this.editService.getReviewsById(this.dataId).subscribe(response => {
this.data = response[0];
console.log(this.data);
});
}
submitUpdate() {
this.editService.putUpdate(this.data).subscribe(response => {
alert('แก้ไขเรียบร้อย');
});
}
}
<header>
<mat-toolbar color="primary">
<a routerLink="/" class="logo"></a>
<span class="example-spacer"></span>
<a class="links side" routerLink="/auth/login" *ngIf="!user">Login</a>
<div>
<a class="links side" *ngIf="user" [matMenuTriggerFor]="menu">
<mat-icon>account_circle</mat-icon>{{ user.fullname }}
</a>
<mat-menu #menu="matMenu">
<button mat-menu-item *ngIf="user?.isAdmin" routerLink="/admin">
admin
</button>
<button mat-menu-item (click)="logout()">logout</button>
</mat-menu>
</div>
</mat-toolbar>
</header>
header {
width: 100%;
.logo {
background-image: url('../../assets/Logo2.png');
width: 100%;
height: 80%;
background-size: contain;
background-repeat: no-repeat;
}
.example-spacer {
flex: 1 1 auto;
}
.links {
color: white;
font-family: 'Helvetica Neue', sans-serif;
font-size: 15px;
font-weight: initial;
letter-spacing: -1px;
line-height: 1;
text-align: center;
padding: 15px;
&.side {
padding: 0 14px;
}
}
.mat-toolbar {
background: black;
}
.mat-icon {
vertical-align: middle;
margin: 0 5px;
}
a {
cursor: pointer;
}
}
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeaderComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Input } from '@angular/core';
import { Router } from '@angular/router';
import { User } from '@app/shared/interfaces';
import { AuthService } from '@app/shared/services';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent {
@Input() user: User | null = null;
constructor(private router: Router, private authService: AuthService) {}
logout(): void {
this.authService.signOut();
this.router.navigateByUrl('/auth/login');
}
}
@import url("https://fonts.googleapis.com/css2?family=Kanit:wght@200;300;600&display=swap");
* {
font-family: "Kanit", sans-serif;
}
.content-topic-page {
padding-left: 30px;
padding-right: 30px;
padding-top: 20px;
padding-bottom: 5px;
}
.title-topic {
padding-left: 15px;
padding-bottom: 15px;
}
.card-content {
padding-bottom: 35px;
padding-left: 30px;
padding-right: 30px;
background-color: white;
border-radius: 10px;
opacity: 0.8;
background-color: #3e3e3e;
}
.content-card {
background-color: #262626;
opacity: 1;
}
table {
width: 100%;
}
th {
text-align: center;
}
.content-flim {
background-color: #262626;
opacity: 1px;
width: 400px;
height: 100px;
font-size: 18px;
font-weight: lighter;
padding: 10px;
border-radius: 6px;
border: none;
outline: none;
}
.content-topic {
margin: auto;
background-color: #262626;
opacity: 1px;
font-size: 18px;
width: 95%;
font-weight: lighter;
padding: 10px;
border-radius: 6px;
border: none;
outline: none;
}
.content-catagory {
margin: auto;
background-color: #262626;
width: 55%;
opacity: 1px;
width: 400px;
height: 100px;
font-size: 18px;
font-weight: lighter;
padding: 10px;
border-radius: 6px;
border: none;
outline: none;
}
.leftcolumn {
float: left;
width: 73%;
}
.rightcolumn {
float: right;
width: 25%;
padding-left: 20px;
}
<div class="content-topic-page">
<div class="row">
<div class="card-content leftcolumn">
<div class="row">
<h2 class="title-topic" style="padding-bottom: 30px">บทความ</h2>
</div>
<div class="row">
<div class="content-topic">
<div style="padding: 10px" >
<a href=""
><label>s</label></a
>
<p>{{ i.t</p>
</div>
</div>
</div>
</div>
<div class="card-content rightcolumn">
<div class="row">
<h2 class="title-topic" style="padding-bottom: 30px">หมวดหมู</h2>
</div>
<div class="row">
<div class="content-topic">
<div style="padding: 10px" >
<a href=""
><label>{{ </label></a
>
</div>
</div>
</div>
</div>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HeroComponent } from './hero.component';
describe('HeroComponent', () => {
let component: HeroComponent;
let fixture: ComponentFixture<HeroComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeroComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HeroComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-hero',
templateUrl: './hero.component.html',
styleUrls: ['./hero.component.css']
})
export class HeroComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
<!-- <td>{{ review.catagory }}</td>
<td>{{ review.namemovie }}</td>
<td>{{ review.title }}</td>
<td>{{ review.description }}</td> -->
<!-- <td>
<div>
<button
mat-raised-button
[routerLink]="['/viewdata', review._id]"
style="background-color: #1cbe3f"
>
ดูข้อมูล
</button>
</div>
</td> -->
<!-- <td>
<button
*ngIf="user_data._id == review.user_id"
mat-raised-button
[routerLink]="['/edit', review._id]"
style="background-color: #ffa000"
>
แก้ไข
</button>
</td>
<td>
<a
role="button"
*ngIf="user_data._id == review.user_id"
mat-raised-button
(click)="deleteData(review._id)"
style="background-color: #d32f2f"
>ลบ</a
>
</td> -->
<div
style="
padding-top: 5%;
padding-left: 10%;
padding-right: 10%;
padding-bottom: 10%;
"
>
<div class="row">
<div class="card-content leftcolumn">
<div class="row">
<h2 class="title-topic">บทความ</h2>
</div>
<div class="row">
<div class="content-topic">
<div style="padding: 10px">
<div *ngFor="let review of reviews" style="padding: 1%">
<mat-card
style="outline: 0"
class="box"
[routerLink]="['/viewdata', review._id]"
><mat-card-title
><h2>{{ review.title }}</h2></mat-card-title
>
<mat-card-content>
{{ review.user_name }}&nbsp; |&nbsp; {{ review.catagory }}&nbsp; | &nbsp;{{ review.createdAt | date:'dd-MM-yyyy'}}
</mat-card-content>
</mat-card>
</div>
</div>
</div>
</div>
</div>
<div class="card-content rightcolumn">
<br />
<br /><br />
<div class="row">
<div class="content-topic">
<div style="padding: 10px">
<section fxLayout="row" fxLayoutGap="5%" class="stycky">
<article class="progress-card" fxFlex="30">
<mat-card fxLayout="column" fxLayoutAlign="center center">
<mat-card-title><h2>เครื่องมือเสริม</h2></mat-card-title>
<mat-card-content>
<mat-list>
<mat-list-item
><button
class="box"
mat-raised-button
routerLink="/create"
style="background-color: #1cbe3f"
>
<mat-icon>post_add</mat-icon>ดูข้อมูล
</button></mat-list-item
>
</mat-list>
</mat-card-content>
</mat-card>
</article>
</section>
</div>
</div>
</div>
</div>
</div>
</div>
.example-icon {
padding: 0 14px;
}
.example-spacer {
flex: 1 1 auto;
}
.example-card {
width: 400px;
margin: 10% auto;
}
.mat-card-title {
font-size: 16px;
}
table {
border-collapse: collapse;
width: 100%;
}
th,
td {
padding: 8px;
border-bottom: 1px solid #ddd;
}
section.stycky {
position: -webkit-sticky; /* Safari */
position: sticky;
top: 0;
}
.leftcolumn {
float: left;
width: 73%;
}
.rightcolumn {
float: right;
width: 25%;
padding-left: 20px;
}
.box:hover {
box-shadow: 0 0 11px rgba(33,33,33,.2);
}
\ No newline at end of file
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HomeComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input } from '@angular/core';
import { HomeService } from '../service/home.service';
import { catagory } from '../../catagory';
import { AuthService } from '@app/shared/services';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
reviews: any;
user_data: any;
catagorys = catagory;
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol','option'];
constructor(private homeService: HomeService, private authService: AuthService) { }
ngOnInit() {
this.fetchData();
this.authService.getUser().subscribe(data => this.user_data = data);
}
fetchData() {
this.homeService.getReviews().subscribe(response => {
this.reviews = response;
});
}
pageroute(data: any) {
console.log(data);
}
deleteData(data: any) {
console.log(data);
this.homeService.deleteReview(data).subscribe((response: {}) => alert('ลบเรียบร้อย'));
this.fetchData();
}
}
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from '@app/shared/services';
@Injectable()
export class AuthHeaderInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = req.clone({
setHeaders: this.authService.getAuthorizationHeaders(),
});
return next.handle(req);
}
}
import { Injectable } from '@angular/core';
import {
HttpEvent,
HttpInterceptor,
HttpHandler,
HttpRequest,
HttpErrorResponse,
} from '@angular/common/http';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class CatchErrorInterceptor implements HttpInterceptor {
constructor(private snackBar: MatSnackBar) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError(this.showSnackBar));
}
private showSnackBar = (response: HttpErrorResponse): Observable<never> => {
const text: string | undefined = response.error?.message ?? response.error.statusText;
if (text) {
this.snackBar.open(text, 'Close', {
duration: 2000,
});
}
return throwError(response);
};
}
import { TestBed } from '@angular/core/testing';
import { CreateService } from './create.service';
describe('CreateService', () => {
let service: CreateService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CreateService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CreateService {
baseUrl = 'http://localhost:4040/api/review';
constructor(public http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
};
postReview(data: any): Observable<any> {
return this.http.post<any>(`${this.baseUrl}`, JSON.stringify(data), this.httpOptions);
}
}
import { TestBed } from '@angular/core/testing';
import { EditService } from './edit.service';
describe('EditService', () => {
let service: EditService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(EditService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class EditService {
baseUrl = 'http://localhost:4040/api/review';
constructor(public http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
};
getReviewsById(_id: any): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/get/${_id}`);
}
putUpdate(data: any): Observable<any> {
return this.http.put(`${this.baseUrl}/update/${data._id}`, data);
}
}
import { TestBed } from '@angular/core/testing';
import { HomeService } from './home.service';
describe('HomeService', () => {
let service: HomeService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(HomeService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class HomeService {
baseUrl = 'http://localhost:4040/api/review';
constructor(public http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
};
getReviews(): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/all`);
}
deleteReview(data: any): Observable<any> {
return this.http.delete<any>(`${this.baseUrl}/delete/${data}`, this.httpOptions);
}
}
import { TestBed } from '@angular/core/testing';
import { ViewdataService } from './viewdata.service';
describe('ViewdataService', () => {
let service: ViewdataService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ViewdataService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ViewdataService {
baseUrl = 'http://localhost:4040/api/review';
constructor(public http: HttpClient) { }
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Accept': 'application/json'
})
};
getReviewsById(_id: any): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/get/${_id}`);
}
}
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AuthService } from '../services';
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService: AuthService) {}
canActivate(): Observable<boolean> {
return this.authService.getUser().pipe(
map(user => {
if (user !== null) {
return true;
}
this.router.navigateByUrl('/auth/login');
return false;
})
);
}
}
export * from './auth.guard';
export * from './user.interface';
export interface User {
_id: string;
fullname: string;
createdAt: string;
roles: string[];
isAdmin: boolean;
}
import { TestBed, inject } from '@angular/core/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthService]
});
});
it('should be created', inject([AuthService], (service: AuthService) => {
expect(service).toBeTruthy();
}));
});
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject, EMPTY } from 'rxjs';
import { tap, pluck } from 'rxjs/operators';
import { User } from '@app/shared/interfaces';
import { TokenStorage } from './token.storage';
interface AuthResponse {
token: string;
user: User;
}
@Injectable({ providedIn: 'root' })
export class AuthService {
private user$ = new BehaviorSubject<User | null>(null);
constructor(private http: HttpClient, private tokenStorage: TokenStorage) {}
login(email: string, password: string): Observable<User> {
return this.http
.post<AuthResponse>('/api/auth/login', { email, password })
.pipe(
tap(({ token, user }) => {
this.setUser(user);
this.tokenStorage.saveToken(token);
}),
pluck('user')
);
}
register(
fullname: string,
email: string,
password: string,
repeatPassword: string
): Observable<User> {
return this.http
.post<AuthResponse>('/api/auth/register', {
fullname,
email,
password,
repeatPassword,
})
.pipe(
tap(({ token, user }) => {
this.setUser(user);
this.tokenStorage.saveToken(token);
}),
pluck('user')
);
}
setUser(user: User | null): void {
if (user) {
user.isAdmin = user.roles.includes('admin');
}
this.user$.next(user);
window.user = user;
}
getUser(): Observable<User | null> {
return this.user$.asObservable();
}
me(): Observable<User> {
const token: string | null = this.tokenStorage.getToken();
if (token === null) {
return EMPTY;
}
return this.http.get<AuthResponse>('/api/auth/me').pipe(
tap(({ user }) => this.setUser(user)),
pluck('user')
);
}
signOut(): void {
this.tokenStorage.signOut();
this.setUser(null);
delete window.user;
}
getAuthorizationHeaders() {
const token: string | null = this.tokenStorage.getToken() || '';
return { Authorization: `Bearer ${token}` };
}
/**
* Let's try to get user's information if he was logged in previously,
* thus we can ensure that the user is able to access the `/` (home) page.
*/
checkTheUserOnTheFirstLoad(): Promise<User> {
return this.me().toPromise();
}
}
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class TokenStorage {
private tokenKey = 'authToken';
signOut(): void {
localStorage.removeItem(this.tokenKey);
localStorage.clear();
}
saveToken(token?: string): void {
if (!token) return;
localStorage.setItem(this.tokenKey, token);
}
getToken(): string | null {
return localStorage.getItem(this.tokenKey);
}
}
export * from './auth/auth.service';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatMenuModule } from '@angular/material/menu';
import { MatTabsModule } from '@angular/material/tabs';
import { MatCardModule } from '@angular/material/card';
import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
import { MatTreeModule } from '@angular/material/tree';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatDialogModule } from '@angular/material/dialog';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatProgressBarModule } from '@angular/material/progress-bar';
@NgModule({
exports: [
FormsModule,
ReactiveFormsModule,
CommonModule,
MatMenuModule,
MatTabsModule,
MatCardModule,
MatListModule,
MatIconModule,
MatTreeModule,
MatInputModule,
MatSelectModule,
MatDialogModule,
MatButtonModule,
MatDividerModule,
MatToolbarModule,
MatSidenavModule,
MatSnackBarModule,
MatExpansionModule,
MatFormFieldModule,
MatProgressBarModule,
],
})
export class SharedModule {}
.example-icon {
padding: 0 14px;
}
.example-spacer {
flex: 1 1 auto;
}
.example-card {
width: 400px;
margin: 10% auto;
}
.mat-card-title {
font-size: 16px;
}
table {
border-collapse: collapse;
width: 100%;
}
th,
td {
padding: 8px;
border-bottom: 1px solid #ddd;
}
section.stycky {
position: -webkit-sticky; /* Safari */
position: sticky;
top: 0;
}
.leftcolumn {
float: left;
width: 73%;
}
.rightcolumn {
float: right;
width: 25%;
}
.box:hover {
box-shadow: 0 0 11px rgba(33,33,33,.2);
}
\ No newline at end of file
<!-- <td>{{ review.catagory }}</td>
<td>{{ review.namemovie }}</td>
<td>{{ review.title }}</td>
<td>{{ review.description }}</td> -->
<!-- <td>
<div>
<button
mat-raised-button
[routerLink]="['/viewdata', review._id]"
style="background-color: #1cbe3f"
>
ดูข้อมูล
</button>
</div>
</td> -->
<!-- <td>
<button
*ngIf="user_data._id == review.user_id"
mat-raised-button
[routerLink]="['/edit', review._id]"
style="background-color: #ffa000"
>
แก้ไข
</button>
</td>
<td>
<a
role="button"
*ngIf="user_data._id == review.user_id"
mat-raised-button
(click)="deleteData(review._id)"
style="background-color: #d32f2f"
>ลบ</a
>
</td> -->
<div
style="
padding-top: 5%;
padding-left: 10%;
padding-right: 10%;
padding-bottom: 10%;
"
>
<div class="row">
<div class="card-content leftcolumn">
<div class="row">
<span>
<a routerLink="/">
<mat-icon>keyboard_backspace</mat-icon>
</a>
</span>
</div>
<div class="row">
<div class="content-topic">
<div style="padding: 10px">
<div style="padding: 1%">
<mat-card
><mat-card-title>{{ data.title }}</mat-card-title>
<mat-card-subtitle
>{{ data.catagory }}&nbsp;|&nbsp;{{
data.createdAt | date:'dd-MM-yyyy'
}}</mat-card-subtitle
>
<mat-card-content>
{{ data.description }}
</mat-card-content>
<mat-card-subtitle
>By &nbsp;{{ data.user_name }}</mat-card-subtitle
>
</mat-card>
</div>
</div>
</div>
</div>
</div>
<div class="card-content rightcolumn">
<br />
<br /><br />
<div class="row">
<div class="content-topic">
<div style="padding: 10px">
<section fxLayout="row" fxLayoutGap="5%" class="stycky">
<article class="progress-card" fxFlex="30">
<mat-card fxLayout="column" fxLayoutAlign="center center">
<mat-card-title><h2>เครื่องมือเสริม</h2></mat-card-title>
<mat-card-content>
<mat-list>
<mat-list-item>
<button
class="box"
mat-raised-button
[routerLink]="['/edit', data._id]"
style="background-color: #ffa000"
>
<mat-icon>create_port</mat-icon>แก้ไข
</button></mat-list-item
>
<mat-list-item
><button
class="box"
mat-raised-button
(click)="deleteData(data._id)"
style="background-color: #d32f2f"
>
<mat-icon>delete</mat-icon>ลบ
</button></mat-list-item
>
</mat-list>
</mat-card-content>
</mat-card>
</article>
</section>
</div>
</div>
</div>
</div>
</div>
</div>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ViewdataComponent } from './viewdata.component';
describe('ViewdataComponent', () => {
let component: ViewdataComponent;
let fixture: ComponentFixture<ViewdataComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ViewdataComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ViewdataComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ViewdataService } from '../service/viewdata.service';
import { HomeService } from '../service/home.service';
@Component({
selector: 'app-viewdata',
templateUrl: './viewdata.component.html',
styleUrls: ['./viewdata.component.css']
})
export class ViewdataComponent implements OnInit {
constructor(private route: ActivatedRoute, private reviewdataService: ViewdataService, private homeService: HomeService, ) { }
dataId: any;
data: any;
ngOnInit(): void {
this.route.paramMap.subscribe(params => {
this.dataId = params.get('dataId');
});
this.reviewdataService.getReviewsById(this.dataId).subscribe(response => {
this.data = response[0];
});
}
deleteData(data: any) {
console.log(data);
this.homeService.deleteReview(data).subscribe((response: {}) => alert('ลบเรียบร้อย'));
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment