├── .gitignore ├── README.md ├── knexfile.js ├── libs └── db.js ├── migrations └── 20210127103201_create_post_table.js ├── package.json ├── pages ├── _app.js ├── api │ ├── hello.js │ └── posts │ │ ├── create.js │ │ ├── delete │ │ └── [id].js │ │ ├── index.js │ │ └── update │ │ └── [id].js └── index.js ├── postcss.config.js ├── public ├── favicon.ico └── vercel.svg ├── styles ├── Home.module.css ├── globals.css └── tailwindcss.css ├── tailwind.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /knexfile.js: -------------------------------------------------------------------------------- 1 | // Update with your config settings. 2 | 3 | module.exports = { 4 | 5 | development: { 6 | client: 'mysql', 7 | connection: { 8 | host: '127.0.0.1', 9 | user: 'root', 10 | password: '', 11 | database: 'nextjs' 12 | } 13 | }, 14 | 15 | staging: { 16 | client: 'postgresql', 17 | connection: { 18 | database: 'my_db', 19 | user: 'username', 20 | password: 'password' 21 | }, 22 | pool: { 23 | min: 2, 24 | max: 10 25 | }, 26 | migrations: { 27 | tableName: 'knex_migrations' 28 | } 29 | }, 30 | 31 | production: { 32 | client: 'postgresql', 33 | connection: { 34 | database: 'my_db', 35 | user: 'username', 36 | password: 'password' 37 | }, 38 | pool: { 39 | min: 2, 40 | max: 10 41 | }, 42 | migrations: { 43 | tableName: 'knex_migrations' 44 | } 45 | } 46 | 47 | }; 48 | -------------------------------------------------------------------------------- /libs/db.js: -------------------------------------------------------------------------------- 1 | const knex = require('knex')({ 2 | client: 'mysql', 3 | connection: { 4 | host : '127.0.0.1', 5 | user : 'root', 6 | password : '', 7 | database : 'nextjs' 8 | } 9 | }); 10 | 11 | 12 | export default knex -------------------------------------------------------------------------------- /migrations/20210127103201_create_post_table.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.createTable('posts', function (table) { 4 | table.increments(); 5 | table.string('title'); 6 | table.text('content'); 7 | table.timestamps(true, true); 8 | }) 9 | }; 10 | 11 | exports.down = function(knex) { 12 | return knex.schema.dropTable('posts') 13 | }; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "knex": "knex" 10 | }, 11 | "dependencies": { 12 | "autoprefixer": "^10.2.3", 13 | "knex": "^0.21.16", 14 | "mysql": "^2.18.1", 15 | "next": "10.0.5", 16 | "postcss": "^8.2.4", 17 | "react": "17.0.1", 18 | "react-dom": "17.0.1", 19 | "sass": "^1.32.5", 20 | "tailwindcss": "^2.0.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | import '../styles/tailwindcss.css' 3 | 4 | function MyApp({ Component, pageProps }) { 5 | return 6 | } 7 | 8 | export default MyApp 9 | -------------------------------------------------------------------------------- /pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default (req, res) => { 4 | res.statusCode = 200 5 | res.json({ 6 | name: 'Cecep Gans', 7 | kelas: 'XII RPL 2', 8 | istri : 4, 9 | anak : 12, 10 | message : 'makanlan disaat kamu lapar' 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /pages/api/posts/create.js: -------------------------------------------------------------------------------- 1 | import db from '../../../libs/db' 2 | 3 | async function handler(req, res) { 4 | if (req.method !== 'POST') 5 | return res.status(405).json({message: 'method not post'}); 6 | 7 | const {title, content} = req.body; 8 | 9 | const create = await db('posts').insert({title, content}); 10 | 11 | 12 | res.status(200); 13 | res.json({message: 'Post create successfully'}) 14 | 15 | } 16 | 17 | export default handler -------------------------------------------------------------------------------- /pages/api/posts/delete/[id].js: -------------------------------------------------------------------------------- 1 | import db from '../../../../libs/db' 2 | 3 | export default async function handler(req, res) { 4 | if (req.method !== 'DELETE') 5 | return res.status(405).json({message: 'method not delete'}); 6 | 7 | const {id} = req.query; 8 | 9 | const data = await db('posts') 10 | .where({id}) 11 | .del() 12 | 13 | res.status(200); 14 | res.json({message: 'post deleted successfully'}) 15 | 16 | } -------------------------------------------------------------------------------- /pages/api/posts/index.js: -------------------------------------------------------------------------------- 1 | import db from '../../../libs/db' 2 | 3 | async function handler(req,res) { 4 | if (req.method !== 'GET') 5 | return res.status(405).json({message: 'method not get'}); 6 | 7 | const data = await db('posts'); 8 | 9 | res.status(200); 10 | res.json({ 11 | message : 'success', 12 | data 13 | }) 14 | 15 | 16 | } 17 | 18 | export default handler -------------------------------------------------------------------------------- /pages/api/posts/update/[id].js: -------------------------------------------------------------------------------- 1 | import db from '../../../../libs/db' 2 | 3 | export default async function handler(req, res) { 4 | if (req.method !== 'PUT') 5 | return res.status(405).json({message: 'method not put'}); 6 | 7 | const {id} = req.query; 8 | const {title, content} = req.body; 9 | 10 | const data = await db('posts') 11 | .where({id}) 12 | .update({title, content}) 13 | 14 | res.status(200); 15 | res.json({message: 'post updated successfully'}) 16 | 17 | } -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import {useState, useEffect} from 'react' 2 | 3 | export default function Home() { 4 | 5 | const [idPost, 6 | setIdPost] = useState(''); 7 | const [title, 8 | setTitle] = useState(''); 9 | const [content, 10 | setContent] = useState(''); 11 | 12 | const [post, 13 | setPost] = useState(null); 14 | 15 | const [prefetch, 16 | setPrefetch] = useState(false); 17 | 18 | const [loadSubmit, 19 | setLoadSubmit] = useState(false); 20 | 21 | const [btnUpdate, 22 | setBtnUpdate] = useState(false) 23 | 24 | useEffect(() => { 25 | getDataApi(); 26 | }, [prefetch]); 27 | 28 | async function getDataApi() { 29 | const getData = await fetch('http://localhost:3000/api/posts'); 30 | const resultPost = await getData.json(); 31 | setPost(resultPost) 32 | } 33 | 34 | const submitValue = async() => { 35 | if (content.length < 1 || title.length < 1) { 36 | alert('data tidak boleh kosong') 37 | } else { 38 | setLoadSubmit(true) 39 | const postData = { 40 | title, 41 | content 42 | } 43 | 44 | await fetch('http://localhost:3000/api/posts/create', { 45 | method: 'POST', 46 | headers: { 47 | 'Accept': 'application/json', 48 | 'Content-Type': 'application/json' 49 | }, 50 | body: JSON.stringify(postData) 51 | }) 52 | 53 | setTitle('') 54 | setContent('') 55 | setPrefetch(!prefetch) 56 | setLoadSubmit(false) 57 | 58 | } 59 | 60 | } 61 | 62 | const handleDelete = async(id) => { 63 | const ask = confirm('Yakin ingin menghapus data?') 64 | 65 | if (ask) { 66 | await fetch('http://localhost:3000/api/posts/delete/' + id, {method: 'DELETE'}).then(response => response.json().then(result => console.log(result))) 67 | setPrefetch(!prefetch) 68 | } 69 | 70 | } 71 | 72 | const handleUpdate = e => { 73 | setBtnUpdate(true) 74 | setIdPost(e.id); 75 | setTitle(e.title); 76 | setContent(e.content); 77 | 78 | } 79 | 80 | const submitUpdate = async() => { 81 | if (content.length < 1 || title.length < 1 || idPost.length < 1) { 82 | alert('data tidak boleh kosong') 83 | } else { 84 | setLoadSubmit(true) 85 | const postData = { 86 | title, 87 | content 88 | } 89 | 90 | await fetch('http://localhost:3000/api/posts/update/' + idPost, { 91 | method: 'PUT', 92 | headers: { 93 | 'Accept': 'application/json', 94 | 'Content-Type': 'application/json' 95 | }, 96 | body: JSON.stringify(postData) 97 | }) 98 | .then(response => response.json()) 99 | .then(result => console.log(result)) 100 | 101 | setTitle('') 102 | setContent('') 103 | setPrefetch(!prefetch) 104 | setLoadSubmit(false) 105 | setBtnUpdate(false) 106 | 107 | } 108 | } 109 | 110 | let contentPost = null 111 | if (post === null) { 112 | contentPost =
113 | Loading... 114 |
115 | } else { 116 | if (post.data.length < 1) { 117 | contentPost =
118 | Tidak ada data 119 |
120 | } 121 | contentPost =
122 | 123 | 124 | 125 | 127 | 129 | 131 | 133 | 134 | 135 | 136 | {post 137 | .data 138 | .map((i, index) => { 139 | // let id = 0 140 | return 143 | 149 | 155 | 161 | 173 | 174 | })} 175 | 176 | 177 |
IdTitleContentActions
145 | Id 147 | {index + 1} 148 | 151 | Title 153 | {i.title} 154 | 157 | Content 159 | {i.content} 160 | 163 | Actions 165 | 166 |  |  169 | 172 |
178 | 179 |
180 | } 181 | 182 | return ( 183 |
184 |
185 |
186 |
187 | 191 |
192 |
193 | setTitle(e.target.value)}> 199 |
200 |
201 |
202 |
203 | 207 |
208 |
209 | 215 |
216 |
217 |
218 |
219 |
220 | {btnUpdate 221 | ? 229 | : } 237 |
238 |
239 |
240 | 241 | {contentPost} 242 | 243 |
244 | ) 245 | } 246 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cecepns/crudnextjs/a7e6f50dc9dc2e1cd7be78c5a12b4fe7dee4118f/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | min-height: 100vh; 3 | padding: 0 0.5rem; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | 10 | .main { 11 | padding: 5rem 0; 12 | flex: 1; 13 | display: flex; 14 | flex-direction: column; 15 | justify-content: center; 16 | align-items: center; 17 | } 18 | 19 | .footer { 20 | width: 100%; 21 | height: 100px; 22 | border-top: 1px solid #eaeaea; 23 | display: flex; 24 | justify-content: center; 25 | align-items: center; 26 | } 27 | 28 | .footer img { 29 | margin-left: 0.5rem; 30 | } 31 | 32 | .footer a { 33 | display: flex; 34 | justify-content: center; 35 | align-items: center; 36 | } 37 | 38 | .title a { 39 | color: #0070f3; 40 | text-decoration: none; 41 | } 42 | 43 | .title a:hover, 44 | .title a:focus, 45 | .title a:active { 46 | text-decoration: underline; 47 | } 48 | 49 | .title { 50 | margin: 0; 51 | line-height: 1.15; 52 | font-size: 4rem; 53 | } 54 | 55 | .title, 56 | .description { 57 | text-align: center; 58 | } 59 | 60 | .description { 61 | line-height: 1.5; 62 | font-size: 1.5rem; 63 | } 64 | 65 | .code { 66 | background: #fafafa; 67 | border-radius: 5px; 68 | padding: 0.75rem; 69 | font-size: 1.1rem; 70 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 71 | Bitstream Vera Sans Mono, Courier New, monospace; 72 | } 73 | 74 | .grid { 75 | display: flex; 76 | align-items: center; 77 | justify-content: center; 78 | flex-wrap: wrap; 79 | max-width: 800px; 80 | margin-top: 3rem; 81 | } 82 | 83 | .card { 84 | margin: 1rem; 85 | flex-basis: 45%; 86 | padding: 1.5rem; 87 | text-align: left; 88 | color: inherit; 89 | text-decoration: none; 90 | border: 1px solid #eaeaea; 91 | border-radius: 10px; 92 | transition: color 0.15s ease, border-color 0.15s ease; 93 | } 94 | 95 | .card:hover, 96 | .card:focus, 97 | .card:active { 98 | color: #0070f3; 99 | border-color: #0070f3; 100 | } 101 | 102 | .card h3 { 103 | margin: 0 0 1rem 0; 104 | font-size: 1.5rem; 105 | } 106 | 107 | .card p { 108 | margin: 0; 109 | font-size: 1.25rem; 110 | line-height: 1.5; 111 | } 112 | 113 | .logo { 114 | height: 1em; 115 | } 116 | 117 | @media (max-width: 600px) { 118 | .grid { 119 | width: 100%; 120 | flex-direction: column; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /styles/tailwindcss.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], 3 | darkMode: false, // or 'media' or 'class' 4 | theme: { 5 | extend: {}, 6 | }, 7 | variants: { 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | } 12 | --------------------------------------------------------------------------------