From 8c16e2497394723be9ea2c982016a6a60c2042b7 Mon Sep 17 00:00:00 2001 From: Diwan Ziyan Date: Thu, 7 Nov 2024 18:11:21 +0700 Subject: [PATCH 1/2] Normalize line endings --- .gitignore | 1 + app/api/getMeasurement/route.js | 23 + app/api/getUser/route.js | 21 + app/api/postMeasurement/route.js | 32 + app/api/postUser/route.js | 36 + app/dashboard/charts/page.jsx | 57 + app/dashboard/layout.jsx | 19 + app/dashboard/map/page.jsx | 55 + app/dashboard/page.jsx | 22 + app/dashboard/setting/page.jsx | 7 + app/dashboard/users/[id]/page.jsx | 117 + app/dashboard/users/add/page.jsx | 174 + app/dashboard/users/page.jsx | 124 + app/globals.css | 0 app/layout.js | 4 +- app/lib/db.js | 17 + app/login/page.jsx | 17 + app/ui/dashboard/card/card.jsx | 16 + app/ui/dashboard/card/card.module.css | 24 + app/ui/dashboard/card/card2.jsx | 16 + app/ui/dashboard/card/card3.jsx | 16 + app/ui/dashboard/charts/charts.module.css | 36 + app/ui/dashboard/dashboard.module.css | 35 + app/ui/dashboard/navbar/navbar.jsx | 33 + app/ui/dashboard/navbar/navbar.module.css | 42 + app/ui/dashboard/pagination/pagination.jsx | 13 + .../pagination/pagination.module.css | 14 + app/ui/dashboard/search/search.jsx | 13 + app/ui/dashboard/search/search.module.css | 16 + .../dashboard/sidebar/menuLink/menuLink.jsx | 19 + .../sidebar/menuLink/menuLink.module.css | 12 + app/ui/dashboard/sidebar/sidebar.jsx | 78 + app/ui/dashboard/sidebar/sidebar.module.css | 59 + app/ui/dashboard/table/table.jsx | 66 + app/ui/dashboard/table/table.module.css | 66 + .../users/addUser/addUser.module.css | 73 + .../users/singleUser/singleUser.module.css | 61 + app/ui/dashboard/users/users.module.css | 49 + app/ui/globals.css | 22 + app/ui/login/login.module.css | 40 + next.config.js | 13 +- package-lock.json | 8607 +++++++++++++++++ package.json | 21 +- .../20241104084912_model1/migration.sql | 12 + .../20241104085219_model2/migration.sql | 10 + .../20241105033522_ngukur1/migration.sql | 15 + .../migration.sql | 8 + .../20241106085823_update1/migration.sql | 30 + .../20241106090256_update2/migration.sql | 20 + .../20241106091051_update3/migration.sql | 8 + .../20241106092156_update4/migration.sql | 10 + .../20241106092254_update5/migration.sql | 14 + .../migration.sql | 19 + .../20241106094127_oiawuijdawd/migration.sql | 20 + prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 37 + request.rest | 34 + 57 files changed, 10416 insertions(+), 10 deletions(-) create mode 100644 app/api/getMeasurement/route.js create mode 100644 app/api/getUser/route.js create mode 100644 app/api/postMeasurement/route.js create mode 100644 app/api/postUser/route.js create mode 100644 app/dashboard/charts/page.jsx create mode 100644 app/dashboard/layout.jsx create mode 100644 app/dashboard/map/page.jsx create mode 100644 app/dashboard/page.jsx create mode 100644 app/dashboard/setting/page.jsx create mode 100644 app/dashboard/users/[id]/page.jsx create mode 100644 app/dashboard/users/add/page.jsx create mode 100644 app/dashboard/users/page.jsx delete mode 100644 app/globals.css create mode 100644 app/lib/db.js create mode 100644 app/login/page.jsx create mode 100644 app/ui/dashboard/card/card.jsx create mode 100644 app/ui/dashboard/card/card.module.css create mode 100644 app/ui/dashboard/card/card2.jsx create mode 100644 app/ui/dashboard/card/card3.jsx create mode 100644 app/ui/dashboard/charts/charts.module.css create mode 100644 app/ui/dashboard/dashboard.module.css create mode 100644 app/ui/dashboard/navbar/navbar.jsx create mode 100644 app/ui/dashboard/navbar/navbar.module.css create mode 100644 app/ui/dashboard/pagination/pagination.jsx create mode 100644 app/ui/dashboard/pagination/pagination.module.css create mode 100644 app/ui/dashboard/search/search.jsx create mode 100644 app/ui/dashboard/search/search.module.css create mode 100644 app/ui/dashboard/sidebar/menuLink/menuLink.jsx create mode 100644 app/ui/dashboard/sidebar/menuLink/menuLink.module.css create mode 100644 app/ui/dashboard/sidebar/sidebar.jsx create mode 100644 app/ui/dashboard/sidebar/sidebar.module.css create mode 100644 app/ui/dashboard/table/table.jsx create mode 100644 app/ui/dashboard/table/table.module.css create mode 100644 app/ui/dashboard/users/addUser/addUser.module.css create mode 100644 app/ui/dashboard/users/singleUser/singleUser.module.css create mode 100644 app/ui/dashboard/users/users.module.css create mode 100644 app/ui/globals.css create mode 100644 app/ui/login/login.module.css create mode 100644 package-lock.json create mode 100644 prisma/migrations/20241104084912_model1/migration.sql create mode 100644 prisma/migrations/20241104085219_model2/migration.sql create mode 100644 prisma/migrations/20241105033522_ngukur1/migration.sql create mode 100644 prisma/migrations/20241105034217_ngukur2update/migration.sql create mode 100644 prisma/migrations/20241106085823_update1/migration.sql create mode 100644 prisma/migrations/20241106090256_update2/migration.sql create mode 100644 prisma/migrations/20241106091051_update3/migration.sql create mode 100644 prisma/migrations/20241106092156_update4/migration.sql create mode 100644 prisma/migrations/20241106092254_update5/migration.sql create mode 100644 prisma/migrations/20241106093941_ngukur2update/migration.sql create mode 100644 prisma/migrations/20241106094127_oiawuijdawd/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma create mode 100644 request.rest diff --git a/.gitignore b/.gitignore index fd3dbb571..a84107af3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ yarn-debug.log* yarn-error.log* # local env files +.env .env*.local # vercel diff --git a/app/api/getMeasurement/route.js b/app/api/getMeasurement/route.js new file mode 100644 index 000000000..a3a4863d3 --- /dev/null +++ b/app/api/getMeasurement/route.js @@ -0,0 +1,23 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +export async function GET(req) { + try { + const measurements = await prisma.measurement.findMany({ + orderBy: { + date: 'desc' // Urutkan berdasarkan tanggal terbaru ke terlama + } + }); + return new Response(JSON.stringify(measurements), { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (error) { + return new Response(JSON.stringify({ error: "Failed to fetch data" }), { + status: 500, + }); + } +} diff --git a/app/api/getUser/route.js b/app/api/getUser/route.js new file mode 100644 index 000000000..e02d9f3bc --- /dev/null +++ b/app/api/getUser/route.js @@ -0,0 +1,21 @@ +import { PrismaClient } from '@prisma/client'; +import { NextResponse } from 'next/server'; + +const prisma = new PrismaClient(); + +export async function GET() { + try { + // Mengambil semua pengguna dari tabel users + const users = await prisma.user.findMany(); + + // Mengembalikan respon JSON dengan data pengguna + return NextResponse.json(users); + } catch (error) { + console.error(error); + // Mengembalikan respon JSON dengan status error + return NextResponse.json({ error: error.message }, { status: 500 }); + } finally { + // Menutup koneksi Prisma setelah selesai + await prisma.$disconnect(); + } +} diff --git a/app/api/postMeasurement/route.js b/app/api/postMeasurement/route.js new file mode 100644 index 000000000..6c133fcc1 --- /dev/null +++ b/app/api/postMeasurement/route.js @@ -0,0 +1,32 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +export async function POST(req) { + const { waterLevel, waterTemp, airTemp, airHumidity, airPressure, windSpeed, curahHujan, date } = await req.json(); + + try { + const newMeasurement = await prisma.measurement.create({ + data: { + waterLevel, + windSpeed, + airTemp, + airHumidity, + airPressure, + waterTemp, + curahHujan, + date: date ? new Date(date) : new Date(), + }, + }); + return new Response(JSON.stringify(newMeasurement), { + status: 201, + headers: { + 'Content-Type': 'application/json', + }, + }); + } catch (error) { + return new Response(JSON.stringify({ error: "Failed to save data" }), { + status: 500, + }); + } + } \ No newline at end of file diff --git a/app/api/postUser/route.js b/app/api/postUser/route.js new file mode 100644 index 000000000..faa92d838 --- /dev/null +++ b/app/api/postUser/route.js @@ -0,0 +1,36 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +export async function POST(req) { + const { username, email, password, role, status } = await req.json(); + + try { + // Menambahkan user baru ke tabel users + const newUser = await prisma.user.create({ + data: { + username, + email, + password, // Pastikan untuk meng-hash password sebelum menyimpannya + role, + status, + }, + }); + + // Mengembalikan respon sukses dengan data user yang baru ditambahkan + return new Response(JSON.stringify({ message: 'User added successfully', user: newUser }), { + status: 201, + headers: { 'Content-Type': 'application/json' }, + }); + } catch (error) { + console.error(error); + // Mengembalikan respon error jika terjadi kesalahan + return new Response(JSON.stringify({ error: 'Failed to add user' }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } finally { + // Menutup koneksi Prisma setelah operasi selesai + await prisma.$disconnect(); + } +} diff --git a/app/dashboard/charts/page.jsx b/app/dashboard/charts/page.jsx new file mode 100644 index 000000000..1c209c461 --- /dev/null +++ b/app/dashboard/charts/page.jsx @@ -0,0 +1,57 @@ +"use client"; +import { useEffect, useState } from "react"; +import styles from "@/app/ui/dashboard/charts/charts.module.css"; +import { LineChart, Line, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer } from "recharts"; + +// Fungsi untuk fetch data dari API +const fetchData = async () => { + const response = await fetch("/api/getMeasurement"); + const result = await response.json(); + return result; +}; + +const ChartsPage = () => { + const [chartData, setChartData] = useState([]); + + useEffect(() => { + fetchData().then((data) => { + // Urutkan data berdasarkan 'date' atau ID dan ambil 10 data terbaru + const sortedData = data + .sort((a, b) => new Date(b.date) - new Date(a.date)) // Mengurutkan berdasarkan tanggal terbaru + .slice(0, 10); // Ambil hanya 10 data terbaru + setChartData(sortedData); + }); + }, []); + + // Fungsi untuk menampilkan tiap chart dengan data dan judul + const renderChart = (title, dataKey) => ( +
+

{title}

+ + + + + + + + + +
+ ); + + return ( +
+
+ {renderChart("Water Level", "waterLevel")} + {renderChart("Water Temperature", "waterTemp")} + {renderChart("Air Temperature", "airTemp")} + {renderChart("Air Humidity", "airHumidity")} + {renderChart("Air Pressure", "airPressure")} + {renderChart("Wind Speed", "windSpeed")} + {renderChart("Rainfall", "curahHujan")} +
+
+ ); +}; + +export default ChartsPage; diff --git a/app/dashboard/layout.jsx b/app/dashboard/layout.jsx new file mode 100644 index 000000000..10b33326d --- /dev/null +++ b/app/dashboard/layout.jsx @@ -0,0 +1,19 @@ +import Navbar from "../ui/dashboard/navbar/navbar" +import Sidebar from "../ui/dashboard/sidebar/sidebar" +import styles from "../ui/dashboard/dashboard.module.css" + +const Layout= ({children}) => { + return ( +
+
+ +
+
+ + {children} +
+
+ ) +} + +export default Layout \ No newline at end of file diff --git a/app/dashboard/map/page.jsx b/app/dashboard/map/page.jsx new file mode 100644 index 000000000..f9ad86b10 --- /dev/null +++ b/app/dashboard/map/page.jsx @@ -0,0 +1,55 @@ +"use client"; // Menandakan sebagai Client Component + +import React, { useEffect, useState } from 'react'; +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; +import 'leaflet/dist/leaflet.css'; +import L from 'leaflet'; + +// Configure Leaflet's default icon +delete L.Icon.Default.prototype._getIconUrl; +L.Icon.Default.mergeOptions({ + iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png', + iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png', + shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png', +}); + +const MapPage = () => { + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + }, []); + + const awsData = [ + { awsNumber: 1, location: [-6.971200, 107.631100], suhu: '28°C', kelembapan: '70%' }, + { awsNumber: 2, location: [-6.972000, 107.632000], suhu: '27°C', kelembapan: '65%' }, + { awsNumber: 3, location: [-6.973500, 107.629800], suhu: '27°C', kelembapan: '65%' }, + // Tambahkan data AWS lainnya di sini + ]; + + if (!isClient) { + return null; // Render nothing on the server + } + + return ( +
+ + + {awsData.map(({ awsNumber, location, suhu, kelembapan }) => ( + + + AWS Station {awsNumber}
+ Suhu: {suhu}
+ Kelembapan: {kelembapan} +
+
+ ))} +
+
+ ); +}; + +export default MapPage; diff --git a/app/dashboard/page.jsx b/app/dashboard/page.jsx new file mode 100644 index 000000000..c9b4383b2 --- /dev/null +++ b/app/dashboard/page.jsx @@ -0,0 +1,22 @@ +import styles from '../ui/dashboard/dashboard.module.css' +import Card from '../ui/dashboard/card/card' +import Table from '../ui/dashboard/table/table' +import Card2 from '../ui/dashboard/card/card2' +import Card3 from '../ui/dashboard/card/card3' + +const Dashboard = () => { + return ( +
+
+
+ + + +
+ + + + ) +} + +export default Dashboard \ No newline at end of file diff --git a/app/dashboard/setting/page.jsx b/app/dashboard/setting/page.jsx new file mode 100644 index 000000000..66db55b9a --- /dev/null +++ b/app/dashboard/setting/page.jsx @@ -0,0 +1,7 @@ +const SettingPage= () => { + return ( +
Ini pengaturan
+ ) +} + +export default SettingPage diff --git a/app/dashboard/users/[id]/page.jsx b/app/dashboard/users/[id]/page.jsx new file mode 100644 index 000000000..a9ba48418 --- /dev/null +++ b/app/dashboard/users/[id]/page.jsx @@ -0,0 +1,117 @@ +"use client" +import styles from '@/app/ui/dashboard/users/singleUser/singleUser.module.css'; +import { useState, useEffect } from 'react'; + +const SingleUserPage = ({ userId }) => { + const [userData, setUserData] = useState(null); + + // Mengambil data pengguna berdasarkan userId + useEffect(() => { + const fetchUserData = async () => { + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/getUser/${userId}`); + const data = await res.json(); + setUserData(data); + }; + fetchUserData(); + }, [userId]); + + const handleUpdate = async (e) => { + e.preventDefault(); + // Mengirim data yang telah diperbarui ke server + try { + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/update/${userId}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(userData), + }); + const result = await response.json(); + console.log('User updated', result); + } catch (error) { + console.error('Error updating user', error); + } + }; + + if (!userData) { + return
Loading...
; // Tampilkan loading jika data belum tersedia + } + + return ( +
+
+ {userData.username} +
+
+
+ + setUserData({ ...userData, username: e.target.value })} + /> + + + setUserData({ ...userData, email: e.target.value })} + /> + + + setUserData({ ...userData, password: e.target.value })} + /> + + + setUserData({ ...userData, phone: e.target.value })} + /> + + + setUserData({ ...userData, address: e.target.value })} + /> + + + + + + + + +
+
+ ); +}; + +export default SingleUserPage; diff --git a/app/dashboard/users/add/page.jsx b/app/dashboard/users/add/page.jsx new file mode 100644 index 000000000..995921632 --- /dev/null +++ b/app/dashboard/users/add/page.jsx @@ -0,0 +1,174 @@ +"use client"; +import { useState } from 'react'; +import { FaEye, FaEyeSlash } from 'react-icons/fa'; +import styles from "@/app/ui/dashboard/users/addUser/addUser.module.css"; + +const AddUserPage = () => { + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [formData, setFormData] = useState({ + username: '', + email: '', + password: '', + confirm: '', + role: 0, // default role as user + status: 1, // default status as active + address: '' + }); + const [errors, setErrors] = useState({}); + + const togglePasswordVisibility = () => { + setShowPassword(!showPassword); + }; + + const toggleConfirmPasswordVisibility = () => { + setShowConfirmPassword(!showConfirmPassword); + }; + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData((prevFormData) => ({ + ...prevFormData, + [name]: name === 'role' || name === 'status' ? parseInt(value) : value // convert role and status to number + })); + + // Password confirmation validation + if (name === 'password' || name === 'confirm') { + if (formData.password !== formData.confirm) { + setErrors((prevErrors) => ({ + ...prevErrors, + confirm: 'Passwords do not match' + })); + } else { + setErrors((prevErrors) => ({ + ...prevErrors, + confirm: '' + })); + } + } + }; + + const validate = () => { + const newErrors = {}; + if (!formData.username) newErrors.username = 'Username is required'; + if (!formData.email) newErrors.email = 'Email is required'; + if (!formData.password) newErrors.password = 'Password is required'; + if (formData.password !== formData.confirm) newErrors.confirm = 'Passwords do not match'; + return newErrors; + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + const validationErrors = validate(); + if (Object.keys(validationErrors).length > 0) { + setErrors(validationErrors); + } else { + try { + const response = await fetch('/api/postUser', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + username: formData.username, + email: formData.email, + password: formData.password, // Pastikan password disimpan sebagai hash di backend + role: formData.role, + status: formData.status, + address: formData.address, + }), + }); + const result = await response.json(); + console.log('Form submitted', result); + } catch (error) { + console.error('Error submitting form', error); + } + } + }; + + return ( +
+
+ + {errors.username &&
{errors.username}
} + + + {errors.email &&
{errors.email}
} + +
+ + + {showPassword ? : } + +
+ {errors.password &&
{errors.password}
} + +
+ + + {showConfirmPassword ? : } + +
+ {errors.confirm &&
{errors.confirm}
} + + + + + +