# Presiapan project

Berikut adalah proyek sederhana yang terdiri dari frontend (React.js) dan backend (Go dengan Fiber) untuk aplikasi todo-list yang bisa melakukan operasi read, create, dan delete dengan PostgreSQL sebagai basis datanya.

#### Struktur Folder

```
todo-frontend/
├── public
│   └── index.html
├── src
│   ├── App.js
│   ├── index.js
│   ├── components
│   │   └── ProductList.js
├── package.json
└── .env

todo-backend/
├── main.go
├── go.mod
├── go.sum
└── database
    └── connection.go
```

#### todo-frontend

**1. File: `todo-frontend/public/index.html`**

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Todo List</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>
```

**2. File: `todo-frontend/src/index.js`**

```javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
```

**3. File: `todo-frontend/src/App.js`**

```javascript
import React from 'react';
import ProductList from './components/ProductList';

function App() {
  return (
    <div className="App">
      <ProductList />
    </div>
  );
}

export default App;
```

**4. File: `todo-frontend/src/components/ProductList.js`**

```javascript
import React, { useState, useEffect } from 'react';
import axios from 'axios';

const ProductList = () => {
  const [products, setProducts] = useState([]);
  const [name, setName] = useState('');
  const [price, setPrice] = useState('');
  const [stock, setStock] = useState('');

  useEffect(() => {
    fetchProducts();
  }, []);

  const fetchProducts = async () => {
    const response = await axios.get(`${process.env.REACT_APP_API_URL}/products`);
    setProducts(response.data);
  };

  const createProduct = async () => {
    const newProduct = { name, price: parseFloat(price), stock: parseInt(stock) };
    await axios.post(`${process.env.REACT_APP_API_URL}/products`, newProduct);
    fetchProducts();
    setName('');
    setPrice('');
    setStock('');
  };

  const deleteProduct = async (id) => {
    await axios.delete(`${process.env.REACT_APP_API_URL}/products/${id}`);
    fetchProducts();
  };

  return (
    <div>
      <h1>Product List</h1>
      <ul>
        {products.map(product => (
          <li key={product.id}>
            {product.name} - ${product.price} - Stock: {product.stock}
            <button onClick={() => deleteProduct(product.id)}>Delete</button>
          </li>
        ))}
      </ul>
      <div>
        <h2>Create Product</h2>
        <input
          type="text"
          placeholder="Name"
          value={name}
          onChange={e => setName(e.target.value)}
        />
        <input
          type="text"
          placeholder="Price"
          value={price}
          onChange={e => setPrice(e.target.value)}
        />
        <input
          type="text"
          placeholder="Stock"
          value={stock}
          onChange={e => setStock(e.target.value)}
        />
        <button onClick={createProduct}>Create</button>
      </div>
    </div>
  );
};

export default ProductList;
```

**5. File: `todo-frontend/package.json`**

```json
{
  "name": "todo-frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.7.2",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
```

**6. File: `todo-frontend/.env`**

```env
REACT_APP_API_URL=http://localhost:3000
```

#### todo-backend

**1. File: `todo-backend/main.go`**

```go
package main

import (
	"todo-backend/database"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"gorm.io/gorm"
)

type Product struct {
	ID    uint    `json:"id" gorm:"primaryKey"`
	Name  string  `json:"name"`
	Price float64 `json:"price"`
	Stock int     `json:"stock"`
}

var DB *gorm.DB

func main() {
	database.Connect()
	DB = database.DB

	DB.AutoMigrate(&Product{})

	app := fiber.New()

	app.Use(cors.New(cors.Config{
		AllowOrigins: "*",
		AllowHeaders: "Origin, Content-Type, Accept",
	}))

	app.Get("/products", GetProducts)
	app.Post("/products", CreateProduct)
	app.Delete("/products/:id", DeleteProduct)

	app.Listen(":3000")
}

func GetProducts(c *fiber.Ctx) error {
	var products []Product
	DB.Find(&products)
	return c.JSON(products)
}

func CreateProduct(c *fiber.Ctx) error {
	product := new(Product)
	if err := c.BodyParser(product); err != nil {
		return c.Status(400).SendString(err.Error())
	}
	DB.Create(&product)
	return c.JSON(product)
}

func DeleteProduct(c *fiber.Ctx) error {
	id := c.Params("id")
	var product Product
	DB.First(&product, id)
	if product.ID == 0 {
		return c.Status(404).SendString("No Product Found with ID")
	}
	DB.Delete(&product)
	return c.SendString("Product Successfully deleted")
}
```

**2. File: `todo-backend/database/connection.go`**

```go
package database

import (
	"fmt"
	"log"
	"os"
	"time"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var DB *gorm.DB

func Connect() {
	var err error
	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable",
		os.Getenv("DB_HOST"),
		os.Getenv("DB_USER"),
		os.Getenv("DB_PASSWORD"),
		os.Getenv("DB_DATABASE"),
		os.Getenv("DB_PORT"),
	)

	DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		log.Fatal("Failed to connect to database: ", err)
	}

	db, err := DB.DB()
	if err != nil {
		log.Fatal("Failed to get database connection: ", err)
	}

	db.SetMaxIdleConns(10)
	db.SetMaxOpenConns(100)
	db.SetConnMaxLifetime(time.Hour)
}
```

**3. File: `todo-backend/go.mod`**

```go
module todo-backend

go 1.18

require (
	github.com/gofiber/fiber/v2 v2.52.4
	gorm.io/driver/postgres v1.5.7
	gorm.io/gorm v1.25.10
)
```

**4. File: `todo-backend/go.sum`**

(Secara otomatis dibuat oleh Go saat Anda menjalankan `go mod tidy` atau menambahkan dependensi.)

#### Menjalankan Proyek

**Backend**

1. **Pastikan PostgreSQL Berjalan**:
   * Pastikan PostgreSQL berjalan dan dapat diakses dengan kredensial yang diberikan.
   * Anda bisa menggunakan Docker untuk menjalankan PostgreSQL:

     ```sh
     docker run --name postgres-db -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=root -e POSTGRES_DB=marketplace -p 5432:5432 -d postgres
     ```
2. **Set Environment Variables**:
   * Buat file `.env` di root folder `todo-backend` dan isi dengan:

     ```env
     DB_HOST=localhost
     DB_PORT=5432
     DB_USER=postgres
     DB_PASSWORD=root
     DB_DATABASE=marketplace
     ```
3. **Jalankan Backend**:
   * Di folder \`todo-back

end\`, jalankan: `sh go mod tidy go run main.go`

**Frontend**

1. **Install Dependencies**:
   * Di folder `todo-frontend`, jalankan:

     ```sh
     npm install
     ```
2. **Jalankan Frontend**:
   * Masih di folder `todo-frontend`, jalankan:

     ```sh
     npm start
     ```

Frontend akan berjalan di `http://localhost:3000` dan akan berkomunikasi dengan backend yang juga berjalan di port 3000.

Dengan mengikuti langkah-langkah di atas, Anda akan memiliki aplikasi todo-list sederhana yang dapat melakukan operasi read, create, dan delete menggunakan React untuk frontend dan Go dengan Fiber untuk backend.

{% embed url="<https://github.com/zakimaliki/fullstack-docker>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zakimaliki.gitbook.io/docker/basic-docker-part-2/presiapan-project.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
