Pages

Sunday, 23 July 2023

LC 01.2

 const { comperePassword, hashPassword} = require('../helpers/bcrypt');

const {Article, Category, User, History} = require('../models')
const {createToken} = require('../helpers/jwt')


const {OAuth2Client} = require('google-auth-library');
const { STRING } = require('sequelize');
const client = new OAuth2Client();

class Controller {
    static async createHistory(req, res, next){
      try {
        const histories = await History.findAll({
          order: [['id', 'DESC']]
        });
        res.json(histories);
      } catch (error) {
        next()
      }
    }

    static async getArticle(req, res, next) {
        try {
          const article = await Article.findAll();
          res.status(200).json(article);
        } catch (err) {
          next(err)
        }
      }

      static async getUser(req, res, next){
        try {
            const user = await User.findAll();
            res.status(200).json(user);
          } catch (err) {
            next(err)
          }
      }

      static async getCategory(req, res, next){
        try {
            const category = await Category.findAll();
            res.status(200).json(category);
          } catch (err) {
            next(err)
          }
      }

      static async postCategory(req, res, next){
        try {
          const data = req.body
          data.authorId = req.user.id
          const createCategory = await Category.create(data)
          if(createCategory){
            await History.create({
              title: 'POST',
              description: `New category with id ${createCategory.id} created`,
              updatedBy: `${req.user.username}`
            })
          }
          res.status(201).json(createCategory)
      } catch (err) {
          next(err)
      }
      }

      static async loginGoogle(req, res){
        try {
            // console.log(req.headers);
            const {google_token} = req.headers
            const ticket = await client.verifyIdToken({
                idToken: google_token,
                audience: '731493170239-2fc94ip6i8tjag20vec2fas5mq3iu4n4.apps.googleusercontent.com',  // Specify the CLIENT_ID of the app that accesses the backend
                // Or, if multiple clients access the backend:
                //[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
            });
            const payload = ticket.getPayload();
            // console.log(payload);
            const {email, name } = payload
            const [user, created] = await User.findOrCreate({
                where: { email : email },
                defaults: {
                  username: name,
                  email:email,
                  password: hashPassword(String(Math.random())),
                  role: 'Staff'
                },
                hooks: false
              });
              const access_token = createToken({id: user.id})
              res.status(200).json(access_token)
        } catch (error) {
            console.log(error);
        }
      }

      static async updatePatch(req, res, next){
        try {
          const id = +req.params.id

          const article = await Article.findByPk(id)

          if(!article){
            res.status(400).json({msg: `Article ${id} not found`})
          }
          const {status} = req.body
          await Article.update({status}, {where: {id}})
          await History.create({
            title: 'PATCH',
            description: `Article with status id ${id} has been updated from ${article.status} to ${status}`,
            updatedBy: `${req.user.username}`
          })
         
          res.status(201).json({msg: `Article ${id} updated from ${article.status} to ${status}`})

        } catch (error) {
          next(error)
        }
      }

      static async updateData(req, res, next){
        try {
          const id = +req.params.id

          const article = await Article.findByPk(id)

          if(!article){
            res.status(400).json({msg: `Article ${id} not found`})
          }

         
          const [isUpdated] = await Article.update(req.body, {where: {id}})
          await History.create({
            title: 'PUT',
            description: `Article id ${id} has updated`,
            updatedBy: `${req.user.username}`
          })
          res.status(201).json({msg: `Article ${id} updated`})

        } catch (error) {
          next(error)
        }
      }

      static async getAllData(req, res, next){
            try {
                const article = await Article.findAll(
                    {include : [Category, User]}
                );
               
                res.status(200).json(article);
            } catch (err) {
                next(err)
            }
      }

      static async createArticle(req, res, next) {
        try {
            const data = req.body
            data.authorId = req.user.id
           
            const createArticle = await Article.create(data)
            if(createArticle){
              await History.create({
                title: 'POST',
                description: `New article with id ${createArticle.id} created`,
                updatedBy: `${req.user.username}`
              })
            }
           
            res.status(201).json(createArticle)
        } catch (err) {
            next(err)
        }
      }

      static async findArticleById (req, res, next){
        try {
            const id = req.params.id
            const article = await Article.findByPk(id);
            if(!article){
                res.status(404).json({message: `Article with id ${id} not found`})
            }else{
                res.status(200).json(article)
            }
        } catch (error) {
            next(error)
        }
      }

      static async deleteById(req, res, next){
        try {
            const id = +req.params.id
            const deleteArticle = await Article.findByPk(id)
            if(!deleteArticle) throw {name: 'NotFound'}
            await Article.destroy({where : {id}})

            res.json({message: `id ${deleteArticle.id} deleted`})
        } catch (err) {
            next(err)
        }

      }

      static async register(req, res, next){
        try {
            const {username, email, password, phoneNumber, address} = req.body
            // console.log(username, email, password, phoneNumber, address);
            const user = await User.create({username, email, password, phoneNumber, address})

            res.status(201).json({
                massage: `User with id ${user.id} has been created`
            })
        } catch (err) {
            next(err)
        }
      }

      static async login(req, res, next){
        try {
            const {email, password} = req.body
            if(!email || !password) throw {name: 'InvalidCredential'}
             
            const user = await User.findOne({where: {email}})
            if(!user) throw {name: 'InvalidCredential'}

            const isPassword = comperePassword(password, user.password)

            if(!isPassword) throw {name: 'InvalidCredential'}

            const access_token = createToken({id: user.id})
            res.json({access_token})

        } catch (err) {
            next(err)
        }
      }

     

}
module.exports = Controller

LC 01.1

 <script>

    export default {
      data (){
      },
      props: ['currentPage', 'categories', 'users'],
     
      methods:{
        toggleDashboard() {
          this.$emit('dashboard')
        }
      }
    }
</script>

<template>
    <ul>
        <li class="nav-item">
        <a class="nav-link" href="#" @click.prevent="toggleDashboard">
          <i class="fas fa-fw fa-database"></i>
          <span>Dashboard</span></a
        >
      </li>
  </ul>
</template>

<style>

</style>

<script>
export default {
  methods: {
    toggleAddNew() {
      this.$emit("formInputArticle");
    },
    toggleEdit() {
      this.$emit("formInputArticle");
    },
    updateArticleStatus(){
      this.$emit('updateStatus')
    }
   
  },
  props: ["articles"],
};
</script>

<template>
  <div class="container" id="showArticles">
    <div class="row">
      <div class="col-6">
        <button class="btn btn-primary mb-2" @click.prevent="toggleAddNew">
          Add New
        </button>
        <h4>Table List Article</h4>
      </div>
    </div>
    <table class="table">
      <thead>
        <tr>
          <th scope="col">No</th>
          <th scope="col">Title</th>
          <th scope="col">Content</th>
          <th scope="col">Image</th>
          <th scope="col">Category</th>
          <th scope="col">Author</th>
          <th scope="col">Status</th>
          <th scope="col">Action</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(article, index) in articles" :key="index">
          <th scope="row">{{ index + 1 }}</th>
          <td>{{ article.title }}</td>
          <td>{{ article.content }}</td>
          <td>
            <img :src="article.imgUrl" class="img-fluid" style="width: 75px" />
          </td>
          <td>{{ article.Category.name }}</td>
          <td>{{ article.User.username }}</td>
          <td>
            <select
              v-model="article.status"
              class="form-control"
              @change.prevent="updateArticleStatus(article.id)"
            >
              <option value="{{ article.status }}">{{ article.status }}</option>
              <option value="Active">Active</option>
              <option value="Archived">Archived</option>
            </select>
          </td>
          <td>
            <a href="#" @click.prevent="toggleEdit(article)"> EDIT </a>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<style></style>

<script>
  export default {
    data (){
      return {
        formInput: {
            title: '',
            content: '',
            imgUrl: '',
            categoryId: ''
      }
      }
    },
    methods: {
      doSubmitArticle(){
        this.$emit('handleAddNew', this.formInput)
      }
    },
    props : ['handleAddNew', 'categories']
     
    }
</script>

<template>
    <div class="container" id="showAddNew">
        <div class="row justify-content-center">
          <div class="col-xl-10 col-lg-12 col-md-9">
            <div class="card o-hidden border-0 shadow-lg my-5">
              <div class="card-body p-0">
                <div class="row">
                  <div class="col-lg-12">
                    <div class="p-5">
                      <div class="text-center">
                        <h1 class="h4 text-gray-900 mb-4">
                          Create a New Article!
                        </h1>
                      </div>
                      <form class="user" @submit.prevent="doSubmitArticle">
                        <div class="form-group row">
                          <div class="col-sm-6 mb-3 mb-sm-0">
                            <input
                              type="text"
                              class="form-control"
                              id="title"
                              name="title"
                              placeholder="Title"
                              v-model="formInput.title"
                            />
                          </div>
                          <div class="col-sm-6">
                            <input
                              type="text"
                              class="form-control"
                              id="content"
                              placeholder="Content"
                              v-model="formInput.content"
                            />
                          </div>
                        </div>
                        <div class="form-group">
                          <input
                            type="text"
                            class="form-control"
                            id="imgUrl"
                            placeholder="Image URL"
                            v-model="formInput.imgUrl"
                          />
                        </div>
                        <div class="form-group">
                          <select class="form-control" v-model="formInput.categoryId" id="categoryId">
                            <option v-for="(category, index) in categories" :key="index" :value="category.id">
                          {{ category.name }}
                        </option>
                          </select>
                        </div>
                        <button type="submit" class="btn btn-primary btn-user btn-block">
                          Submit Article
                        </button>
                      </form>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
</template>

<style>
</style>

LC 01

 <script>

import axios from "axios";
import Login from "./components/Login.vue";
import Navbar from "./components/Navbar.vue";

export default {
  data() {
    return {
      currentPage: "",
      articles: [],
      usersFetch: [],
      categories: [],
      logs: [],
      articlesData: [],
      categoriesData: [],
      message: "Hallo Vue",
    };
  },
  methods: {
    async handleLogin(login) {
      try {
        const { data } = await axios({
          method: "post",
          url: `http://localhost:3000/login`,
          data: login,
        });
        const accessToken = data.access_token;
        localStorage.setItem("access_token", accessToken);

        if (data) {
          const userName = data.name; // Assuming the user's name is available in the response
          Swal.fire("Good job!", `You are logged in as ${login.email}`, "success");

          this.currentPage = "navbar";
          this.currentPage = "dashboard";
          // this.currentPage = "navbar";
         
          this.fetchArticle();
          this.fetchCategory();
        }
      } catch (error) {
        Swal.fire("Error", "Login failed. Please try again.", "error");
        console.log(error);
      }
    },
    async updateArticleStatus(article) {
   
    async fetchArticle() {
      try {
        const response = await axios({
          method: "GET",
          url: "http://localhost:3000/categories/user",
          headers: {
            access_token: localStorage.access_token,
          },
        });
        this.articles = response.data;
       
       
      } catch (error) {
        console.log(error);
       
      }
    },
    async handleRegister(formRegister) {
      try {
        const { data } = await axios({
        method: "POST",
        url: `http://localhost:3000/register`,
        data: formRegister,
      });

      Swal.fire(
          'Success',
          `User is has created`,
          'success'
        );

      formRegister.username = "";
      formRegister.email = "";
      formRegister.password = "";
      formRegister.address = "";
      formRegister.phoneNumber = "";
      } catch (error) {
        console.log(error);
        Swal.fire(
          'Error',
          'Failed to create user status',
          'error'
        );
      }
    },
    async handleAddNew(dataInput) {
      try {
        await axios({
          method: "post",
          url: `http://localhost:3000/news`,
          data: dataInput,
          headers: {
            access_token: localStorage.getItem("access_token"),
          },
        });
        Swal.fire(
          'Success',
          `Article has created`,
          'success'
        );
        this.currentPage = "article";
       
        this.fetchArticle();
      } catch (error) {
        console.log(error);
        Swal.fire(
          'Error',
          'Failed to create article',
          'error'
        );
      }
    },
   
    moveToDashboard() {
      this.currentPage = "dashboard";
    },
    moveToArticles() {
      this.currentPage = "article";
      this.fetchArticle();
    },
    toggleLogout() {
      localStorage.removeItem("access_token");
      this.currentPage = "login";
      this.formLogin.email = "";
      this.formLogin.password = "";
    },
  },
  created() {
    if (localStorage.access_token) {
      this.currentPage = "navbar";
    } else {
      this.currentPage = "login";
    }
  },
  components: {
    Login
  },
};
</script>

<template>
  <div class="containerr">
    <Navbar
      @dashboard="moveToDashboard"
      @article="moveToArticles"
      @category="moveToCategory"
      @log="moveToLog"
      @logout="toggleLogout"
      :users="user"
    />
    <div class="page">
      <Login
        @handleLogin="handleLogin"
        @handleRegister="handleRegister"
        v-if="currentPage === `login`"
      />
      <Article
        :articles="articles"
        @updateStatus="updateArticleStatus"
        v-if="currentPage === `article`"
        @formInputArticle="formArticle"
      />
      <Category :categories="categories" v-if="currentPage === `categorie`" />
      <Logs :logs="logs" v-if="currentPage === `log`" />
      <FormArticle
        v-if="currentPage === `formInput`"
        @handleAddNew="handleAddNew"
        :categories="categories"
      />
      <Dashboard
        v-if="currentPage === `dashboard`"
        :articles="articles"
        :categories="categories"
      />
      <!-- <Footer /> -->
    </div>
  </div>
</template>

<style scoped>
.containerr {
  display: flex;
}
</style>

Tuesday, 4 July 2023

input tambahan

app.set('view engine', 'ejs')
app.use(express.urlencoded({ extended: false }))

 Asosiasi

static associate(models) {
      // define association here
      Employee.belongsTo(models.Store, { foreignKey: "StoreId" })
    }

untuk model Store dan employee

static associate(models) {
      // define association here
      Store.hasMany(models.Employee, {foreignKey: "StoreId"})
    }

Untuk validasi

Store.init({
    name: {
      type: DataTypes.STRING,
      allowNull: false,
      validate: {
        notNull: {
          msg: 'Name is required',
        },
        notEmpty: {
          msg: 'Name is required',
        }
      }
    },
    position: {
      type : DataTypes.STRING,
      allowNull : false,
      validate: {
        notNull : {
          msg: "Position is require"
        },
        notEmpty: {
          msg: "Position is require"
        },
        validPosition(value){
          if((this.position === 'S2' || this.position === 'S3') &&
          (value !== 'manager' && value !== 'CEO')){
            throw new Error('S2 & S3 only can be Manager or CEO')
          }
        }
      }
    },
    //untuk hooks
    sequelize,
    modelName: 'Store',
    hooks : {
      beforeCreate : function (store, opt){
        if(store.category === 'Mart'){
          store.code = `001-${new Date().getTime()}`
        }
        if(store.category === 'Midi'){
          store.code = `002-${new Date().getTime()}`
        }
        if(store.category === 'Express'){
          store.code = `003-${new Date().getTime()}`
        }
      },
      afterCreate : function(after, opt){
      }
    }
})

untuk errors
.catch((err) => {
            if (err.name === 'SequelizeValidationError'){
                const errors = err.errors.map(error => {
                    return error.message
                })
                res.send(errors)
                // console.log(errors);
               
            }else{
                res.send(err)
            }
        })
atau

.catch((err) => {
            if (err.name === 'SequelizeValidationError') {
                let errors = err.errors.map(el => el.message)
                res.redirect(`/stores/${storeId}/employees/${employeeId}/edit?errors=${errors}`)
            } else {
                res.send(err);
            }
          });

untuk update
Employee.update(
          { firstName, lastName, dateOfBirth, education, position, salary },
          { where: { id: employeeId } }
        )

utnuk delete
Art.destroy({
            where: {
                id: id
            }
        })

untuk tambah data

const {name, artist, date, photo, placeOfOrigin, description} = req.body
        Art.create({name, artist, date, photo, placeOfOrigin, description})

untuk search
static showArts(req, res){
        let {title, artist} = req.query
        let dataArt
        let option = {
        }

        if(title){
            option.where = {
                name:{
                    [Op.iLike] : `%${title}%`
                }
            }
        }

        if(artist){
            option.where = {
                artist:{
                    [Op.like] : `%${artist}%`
                }
            }
        }

        option.order = [['date', 'asc']]
        Art.findAll(option)
        .then((data) => {
            dataArt = data
            return Art.alert()
        })
        .then((alert) => {
            const { sumArt, oldYear, latestYear } = alert[0].dataValues;
            res.render('home',{data : dataArt, alert :{ sumArt, oldYear, latestYear }, title: 'List Of Art'})
        })
        .catch((err) => {
            res.send(err)
        })
    }

Tuesday, 27 June 2023

Membuat validasi

 Membuat validasi

static validation(title, bandName){
       
        const errors = []
       
        if(!title){
            errors.push(`Title is required.`)
        }
        if(!bandName){
            errors.push(`Band name is required.`)
        }
        return errors;
    }

Javascript Validator is easy to use tool to validate JavaScript code. Copy, Paste and
Validate JavaScript. This JS linter checks the js code and highlights errors as well
as shows the detail of the error in a plain and easy-to-read gradient table.



didalam add masukkan ini

static createSong(title, bandName, duration, genre, createDate, lyric, imageUrl, LabelId, cb) {
       
const errors = SongModel.validation(title, bandName)

        if(errors.length){
            cb({
                code: 1,
                errors
            })
        }else{
            let queryInsertSong = `
        insert into "Songs" ("title", "bandName", "duration", "genre", "createdDate", "lyric", "imageUrl", "totalVote", "LabelId")
        values ('${title}', '${bandName}', ${duration}, '${genre}', '${createDate}', '${lyric}', '${imageUrl}', 0, ${LabelId});`

        pool.query(queryInsertSong, (err) => {
            if (err) {
                cb({
                    code: 2,
                    err
                })
            } else {
                cb(null)
            }
        })
        }
    }

JS Validator uses JavaScript libs for validating and presenting warnings and errors.
It process and validates js in a browser environment. Just Paste your JS code and
click Validate JS. This tool does not send js code to the server for validating.
In the case of js file upload, Browser reads the file and for URL upload,
it sends the javascript URL to server and return js data and then run the
validation logic



didalam controllers

static addReadSong(req, res){
        const {errors} = req.query
        LabelModel.viewLabel((err, data) => {
            if(err){
                res.send(err)
            }else{
                res.render('add-song', { data: data, errors })
            }
        })
    }

    static addSong(req, res){
        const { title, bandName, duration, genre, createDate, lyric, imageUrl, LabelId } = req.body
        // console.log(imageUrl, 'dari control');
        SongModel.createSong(title, bandName, duration, genre, createDate, lyric, imageUrl, LabelId, (err) => {
            if(err){
                if(err.code === 1){
                    res.redirect(`/songs/add?errors=${err.errors}`)
                }else{
                    res.send(err)
                }
            }else{
                res.redirect('/songs')
            }
        })
    }


didalam ejsnya
<% if(errors) { %>
            <% errors.split(',').forEach(el => { %>
              <b><p class="text-danger"><%= el %></p></b>
              <% }) %>
          <% } %>

Sunday, 25 June 2023

CRUD WTIH EJS

Langkah Langkah :
1. Membuat koneksi kedatabse dengan sintak berikut ini :
const {Pool} = require('pg')

const pool = new Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'songLabel',
    password: 'postgres',
    port: 5432,
    idleTimeoutMillis : 500
  })

//   cek koneksi
//   pool.query('SELECT NOW()', (err, result) => {
//     console.log(err, result);
//   })

module.exports = pool

2. Membuat migration untuk membuat tabel database


const pool = require("./config/connection");

let queryTableLabel = `create table "Labels" (
    "id" serial primary key,
    "name" varchar (120) not null,
    "since" date,
    "city" varchar (120) not null
);`

let queryTableSong = `create table "Songs" (
    "id" serial primary key,
    "title" varchar (100),
    "bandName" varchar (100),
    "duration" integer,
    "genre" varchar (10),
    "createdDate" date,
    "lyric" text,
    "imageUrl" varchar (150),
    "totalVote" integer,
    "LabelId" integer not null references "Labels" ("id")
);`

let dropTable = `DROP TABLE IF EXISTS "Labels", "Songs"`

pool.query(dropTable, (err, res) => {
    if(err){
        console.log(err, `EROR DROP TABLE`);
    }else{
        pool.query(queryTableLabel, (err, res) => {
            if(err){
                console.log(err, `EROR CREATE TABEL LABELS`);
            }else{
                console.log(`SUCCESS CREATE TABLE LABELS`);
                pool.query(queryTableSong, (err, res) => {
                    if(err){
                        console.log(`EROR CREATE TABEL SONGS`);
                    }else{
                        console.log(`SUCCESS CREATE TABLE SONGS`);
                    }
                })
            }
        })
    }
})

Pengembangan framework JavaScript, yang terdiri dari library kode JavaScript, memungkinkan developer menggunakan kode JS siap pakai dalam proyek mereka. Proses yang harus dilalui pun menjadi lebih cepat dan efisien karena mereka tidak perlu menulis kode dari nol.

Setiap framework JavaScript memiliki fitur yang berfungsi untuk menyederhanakan proses development dan debugging.


3. Membuat sedding untuk dapat melakukan insert data ke databse dari data json
const pool = require("./config/connection");
const fs = require('fs')

let label = fs.readFileSync("./data/labels.json", "utf-8")
label = JSON.parse(label)

let valueLabel = label.map(el => {
    return `('${el.name}', '${el.since}', '${el.city}')`
}).join(",")
let queryInsertLabel = `insert into "Labels" ("name", "since", "city")
values ${valueLabel};`

pool.query(queryInsertLabel, (err, result) => {
    if(err){
        console.log(err, 'EROR INSERT DATA LABELS');
    }else{
        console.log('SUCCESS INSERT DATA LABELS');
        let song = fs.readFileSync("./data/songs.json", "utf-8")
        song = JSON.parse(song)
        let valueSong = song.map(el => {
            return `('${el.title}', '${el.bandName}', ${el.duration}, '${el.genre}',
        '${el.createdDate}', '${el.lyric}', '${el.imageUrl}', ${el.totalVote}, ${el.LabelId})`
        }).join(",")

        let queryInsertSong = `insert into "Songs" ("title", "bandName", "duration",
        "genre", "createdDate", "lyric", "imageUrl", "totalVote", "LabelId")
        values ${valueSong}; `

        pool.query(queryInsertSong, (err, result) => {
            if(err){
                console.log(err, 'EROR INSERT DATA SONGS');
            }else{
                console.log('SUCCESS INSERT DATA SONGS');
            }
        })
    }
})

Meskipun Anda bisa menggunakan HTML dan CSS saja untuk membuat website, halaman yang dihasilkan hanya akan memiliki tampilan statis tanpa adanya JavaScript. Bahasa pemrograman ini memungkinkan pengunjung berinteraksi dengan halaman web, dan Anda pun bisa menyajikan user experience yang lebih baik.

4. Membuat class untuk model
const { fail } = require("assert")

class Label{
    constructor(id, name, since, city){
        this.id = id
        this.name = name
        this.since = since
        this.city = city
    }
    get sinceLocale(){
        const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
        return this.since.toLocaleDateString('id-ID', options)
    }
}

class LabelDetailDuration extends Label{
    constructor(id, name, since, city, averageDuration, minDuration, maxDuration){
        super(id, name, since, city)
        this.averageDuration = +averageDuration
        this.minDuration = +minDuration
        this.maxDuration = +maxDuration
    }
}

class Song{
    constructor(id, title, bandName, duration, genre, totalVote){
        this.id = id
        this.title = title
        this.bandName = bandName
        this.duration = duration
        this.genre = genre
        this.totalVote = totalVote
    }
}

class SongDetail extends Song{
    constructor(id, title, bandName, duration, genre, totalVote, createdDate, lyric, imageUrl, LabelId, LabelName){
        super(id, title, bandName, duration, genre, totalVote)
        this.createdDate = createdDate
        this.lyric = lyric
        this.imageUrl = imageUrl
        this.LabelId = LabelId
        this.LabelName = LabelName
    }

    get dateCreate(){
        const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
        return this.createdDate.toLocaleDateString('id-ID', options)
    }

    get inputDate() {
        const date = new Date(this.createdDate);
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
      }
    };


class Factory{
    static createLabel(id, name, since, city){
        return new Label(id, name, since, city)
    }

    static createSong(id, title, bandName, duration, genre, totalVote){
        return new Song(id, title, bandName, duration, genre, totalVote)
    }

    static createLabelDetail(id, name, since, city, averageDuration, minDuration, maxDuration){
        return new LabelDetailDuration(id, name, since, city, averageDuration, minDuration, maxDuration)
    }

    static createSongDetail(id, title, bandName, duration, genre, totalVote, createdDate, lyric, imageUrl, LabelId, LabelName){
        return new SongDetail(id, title, bandName, duration, genre, totalVote, createdDate, lyric, imageUrl, LabelId, LabelName)
    }
}

module.exports = Factory




5. pada app.js untuk mengatur rute
// Happy coding guys
const express = require('express')
const pool = require('./config/connection')
const Factory = require('./models/class')
const app = express()
const port = 3000

app.set('view engine', 'ejs')
app.use(express.urlencoded({ extended: false }))

app.get('/labels', (req, res) => {
    const queryLabels = `select * from "Labels"`
    pool.query(queryLabels, (err, result) => {
        if (err) {
            res.send(err)
        } else {
            result = result.rows.map(el => {
                return Factory.createLabel(el.id, el.name, el.since, el.city)
            })
            res.render('labels', { data: result })
        }
    })

})




app.get('/labels/detail', (req, res) => {
    let queryLabelDetail = `select l.*, max(s.duration) as max, min(s.duration) as min, cast(avg(s.duration)
                            as float) as avg from "Labels" l left join "Songs" s on s."LabelId" = l.id
                            group by l.id;`
    pool.query(queryLabelDetail, (err, result) => {
        if (err) {
            res.send(err)
        } else {
            // console.log(result);
            result = result.rows.map(el => {
                return Factory.createLabelDetail(el.id, el.name, el.since, el.city, el.avg, el.min, el.max)
            })
            res.render('label-details', { data: result })
        }
    })
})


app.get('/songs', (req, res) => {
    const {search} = req.query
    let querySongs = ``
    if(search){
        querySongs = `select * from "Songs" where "title" ilike '%${search}%'`
    }else{
        querySongs = `select * from "Songs" order by "totalVote" asc;`
    }
   
    pool.query(querySongs, (err, result) => {
        if (err) {
            res.send(err)
        } else {
            result = result.rows.map(el => {
                return Factory.createSong(el.id, el.title, el.bandName,
                el.duration, el.genre, el.totalVote)
            })
            res.render('songs', { data: result })
        }
    })
})


Dengan JavaScript, Anda juga bisa meningkatkan pengalaman browsing pengunjung menggunakan cookie. Untuk membuat, membaca, dan menghapus cookie dalam JavaScript, diperlukan properti document.cookie yang berfungsi sebagai “getter and setter” nilai cookie.

app.get('/songs/add', (req, res) => {
    let queryLabel = `select * from "Labels";`
    pool.query(queryLabel, (err, result) => {
        if (err) {
            res.render(err)
        } else {
            result = result.rows.map(el => {
                return Factory.createLabel(el.id, el.name, el.since, el.city)
            })
            res.render('add-song', { data: result })
        }
    })

})

app.post('/songs/add', (req, res) => {
    const { title, bandName, duration, genre, createDate, lyric, imageUrl, LabelId } = req.body
    let queryInsertSong = `
        insert into "Songs" ("title", "bandName", "duration", "genre", "createdDate", "lyric", "imageUrl", "totalVote", "LabelId")
        values ('${title}', '${bandName}', ${duration}, '${genre}', '${createDate}', '${lyric}', '${imageUrl}', 0, ${LabelId});`

    pool.query(queryInsertSong, (err, result) => {
        if(err){
            res.render(err)
        }else{
            res.redirect('/songs')
        }
    })
})


Menurut informasi dari website resmi Java, bahasa pemrograman JavaScript yang dikembangkan oleh Netscape, Inc. bukanlah bagian dari platform Java yang dikembangkan oleh Sun Microsystems (sebelum diakuisisi Oracle).

Perbedaan Java dan JavaScript adalah, Java merupakan bahasa pemrograman, sedangkan JavaScript merupakan skrip pemrograman. Kode JavaScript ditulis dalam teks dan bisa langsung diinterpretasikan browser, sedangkan Java harus di-compile menjadi bytecode yang bisa dipahami dan dijalankan komputer.


app.get('/songs/:id', (req, res) => {
    const idSong = req.params.id
    const queryById = `select s.*, l."name"  from "Songs" s join "Labels" l on l.id = s."LabelId"
    where s.id = ${idSong};`
    pool.query(queryById, (err, result) => {
        if(err){
            res.render(err)
        }else{
            result = result.rows.map(el => {
                return Factory.createSongDetail(el.id, el.title, el.bandName, el.duration, el.genre,
                el.totalVote, el.createdDate, el.lyric, el.imageUrl, el.LabelId, el.name)
            })
            // console.log(result);
            res.render('song-details', {data: result})
        }
    })
})

app.get('/songs/:id/vote', (req, res) => {
    const idSong = +req.params.id
    let queryVote = `update  "Songs" set "totalVote" = "totalVote" + 1 where id = ${idSong}`
    pool.query(queryVote, (err, result) => {
        if(err){
            res.render(err)
        }else{
            res.redirect(`/songs/${idSong}`)
        }
    })
})

app.get('/songs/:id/edit', (req, res) => {
    const idSong = +req.params.id
    let queryLabel = `select * from "Labels";`
    const querySong = `select s.*, l."name"  from "Songs" s join "Labels" l on l.id = s."LabelId" where s.id = ${idSong};`
    pool.query(queryLabel, (err, label) => {
        if (err) {
            res.render(err)
        } else {
            label = label.rows.map(el => {
                return Factory.createLabel(el.id, el.name, el.since, el.city)
            })
            pool.query(querySong, (err, song) => {
                if(err){
                    res.send(err)
                }else{
                    console.log(song);
                    song = song.rows.map(el => {
                        return Factory.createSongDetail(el.id, el.title, el.bandName, el.duration, el.genre,
                        el.totalVote, el.createdDate, el.lyric, el.imageUrl, el.LabelId, el.name)
                    })
                    res.render('edit-song', {label, song: song[0] })
                }
            })
        }
    })
})



Nah, bahasa scripting termasuk ke dalam bahasa pemrograman, tapi digunakan untuk memanipulasi, menyesuaikan, dan mengotomatiskan apa yang sudah ada di sistem. Sedangkan bahasa pemrograman biasanya digunakan untuk membuat program dari nol.

app.post('/songs/:id/edit', (req, res) => {
    const idSong = +req.params.id
    const {title, bandName, duration, genre, createDate, lyric, imageUrl, LabelId} = req.body
    const queryUpdateSong = `update "Songs" set
    "title" = '${title}',
    "bandName" = '${bandName}',
    "duration" = ${duration},
    "genre" = '${genre}',
    "createdDate" =  '${createDate}',
    "lyric" = '${lyric}',
    "imageUrl" = '${imageUrl}',
    "LabelId" = ${LabelId}
    where id = ${idSong};`

    pool.query(queryUpdateSong, (err, result) => {
        if(err){
            res.send(err)
        }else{
            res.redirect('/songs')
        }
    })
})

app.get('/songs/:id/delete', (req, res) => {
    const idSong = +req.params.id
    const queryDelete = `delete from "Songs" where id = ${idSong};`
    pool.query(queryDelete, (err, result) => {
        if(err){
            res.render(err)
        }else{
            res.redirect('/songs')
        }
    })
})

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})