From 9adb7ec8c06ed9c2f5b3649c0a7a721fce1de4db Mon Sep 17 00:00:00 2001 From: fdhliakbar Date: Fri, 23 Jan 2026 14:51:03 +0700 Subject: [PATCH] feat: fetch superadmin & crete table vehicle with model --- src/api/companies.ts | 35 ++ src/api/device.ts | 1 + src/api/vehicle.ts | 57 +- src/components/leftbar.tsx | 2 +- src/components/vehicle-map .tsx | 37 +- .../kendaraan/dialog-kendaraan-detail.tsx | 0 src/pages/kendaraan/dialog-kendaraan-edit.tsx | 505 +++++++++++++++ .../kendaraan/dialog-kendaraan-tambah.tsx | 390 ++++++++++++ .../dialog-model-kendaraan-tambah.tsx | 260 ++++++++ src/pages/kendaraan/lihat-kendaraan.tsx | 5 + src/pages/kendaraan/list-kendaraan.tsx | 582 ++++++++++++++++++ src/pages/kendaraan/list-model-kendaraan.tsx | 238 +++++++ .../manajemen-device/manajemen-device.tsx | 19 + .../dialog-organisasi-edit.tsx | 171 +++-- .../dialog-organisasi-tambah.tsx | 93 ++- .../manajemen-organisasi.tsx | 358 ++++++----- src/pages/odometer/list-odometer.tsx | 304 +++++++++ src/route/app-routes.tsx | 6 + 18 files changed, 2841 insertions(+), 222 deletions(-) create mode 100644 src/api/companies.ts create mode 100644 src/api/device.ts create mode 100644 src/pages/kendaraan/dialog-kendaraan-detail.tsx create mode 100644 src/pages/kendaraan/dialog-kendaraan-edit.tsx create mode 100644 src/pages/kendaraan/dialog-kendaraan-tambah.tsx create mode 100644 src/pages/kendaraan/dialog-model-kendaraan-tambah.tsx create mode 100644 src/pages/kendaraan/lihat-kendaraan.tsx create mode 100644 src/pages/kendaraan/list-kendaraan.tsx create mode 100644 src/pages/kendaraan/list-model-kendaraan.tsx create mode 100644 src/pages/manajemen-device/manajemen-device.tsx create mode 100644 src/pages/odometer/list-odometer.tsx diff --git a/src/api/companies.ts b/src/api/companies.ts new file mode 100644 index 0000000..87dae14 --- /dev/null +++ b/src/api/companies.ts @@ -0,0 +1,35 @@ +import axios from "@/lib/axios"; + +export interface CreateCompanyPayload { + name: string; + address: string; + email: string; + codeConfirm: string; + phoneNumber: string; + industryType: string; + picName: string; + picPhone: string; + startDate: string; + endDate: string; + isActive: boolean; +} + +export const getCompaniesApi = async () => { + const res = await axios.get("/companies"); + return res.data.data; +} + +export const createCompanyApi = async (companyData: CreateCompanyPayload) => { + const res = await axios.post("/companies", companyData); + return res.data.data; +} + +export const updateCompanyApi = async (id: number, companyData: Partial) => { + const res = await axios.put(`/companies/${id}`, companyData); + return res.data.data; +} + +export const deleteCompanyApi = async (id: number) => { + const res = await axios.delete(`/companies/${id}`); + return res.data.data; +} \ No newline at end of file diff --git a/src/api/device.ts b/src/api/device.ts new file mode 100644 index 0000000..e3fe24e --- /dev/null +++ b/src/api/device.ts @@ -0,0 +1 @@ +// nanti tunggu ui/ux ada \ No newline at end of file diff --git a/src/api/vehicle.ts b/src/api/vehicle.ts index 171d3cf..5658408 100644 --- a/src/api/vehicle.ts +++ b/src/api/vehicle.ts @@ -45,10 +45,6 @@ export const getMachineActiveVehicle = async (companyId: number) => { return res.data.data; }; -export const getVehicleDetail = async (vehicleId: number) => { - const res = await axios.get(`vehicles/vehicle-detail/${vehicleId}`); - return res.data.data; -}; export const getVehicleActivityReport = async ( vehicleId: number, date: string ) => { const res = await axios.get(`vehicles/activity/trip-report/${vehicleId}/date`, { params: { date } }); @@ -77,3 +73,56 @@ export const toggleVehicleStarter = async ( ); return res.data; }; + +// api to get detailed list of vehicles by company - super admin +export const getDetailedListVehiclesByCompany = async (companyId: number) => { + const res = await axios.get(`vehicles/list/sa/${companyId}/detailed`); + return res.data.data; +}; + +// count company - super admin + +export const getVehiclesCountAllCompanies = async () => { + const res = await axios.get(`vehicles/count/sa`); + return res.data.data.vehicle_count ?? 0; +}; + +export const getVehiclesCountByCompany = async (companyId: number) => { + const res = await axios.get(`vehicles/count/sa/${companyId}`); + return res.data.data.vehicle_count ?? 0; +} + +export const deleteVehicleById = async (vehicleId: number) => { + const res = await axios.delete(`vehicles/${vehicleId}`); + return res.data; +} + +// Vehicles API +export const editVehiclesById = async (vehicleId: number, vehicleData: object) => { + const res = await axios.put(`vehicles/${vehicleId}`, vehicleData); + return res.data; +} + +export const getVehicleDetail = async (vehicleId: number, companyId: number) => { + const res = await axios.get(`vehicles/vehicle-details/${vehicleId}`, { + params: { companyId } + }); + return res.data.data; +}; + +// MODEL VEHICLE API + +export const addVehicleModel = async (modelData: number) => { + const res = await axios.post(`vehicles/models`, modelData); + return res.data; +} + +export const editVehicleModelById = async (modelId: number, modelData: number) => { + const res = await axios.put(`vehicles/models/${modelId}`, modelData); + return res.data; +} + +export const deleteVehicleModelById = async (modelId: number) => { + const res = await axios.delete(`vehicles/models/${modelId}`); + return res.data; +} \ No newline at end of file diff --git a/src/components/leftbar.tsx b/src/components/leftbar.tsx index c41f6e1..79a1adc 100644 --- a/src/components/leftbar.tsx +++ b/src/components/leftbar.tsx @@ -36,7 +36,7 @@ const data = { }, { title: "Manajemen Device", - url: "/device-management", + url: "/manajemen-device", icon: Car, }, { diff --git a/src/components/vehicle-map .tsx b/src/components/vehicle-map .tsx index eddcafa..25d429b 100644 --- a/src/components/vehicle-map .tsx +++ b/src/components/vehicle-map .tsx @@ -4,7 +4,20 @@ import L from "leaflet"; import "leaflet/dist/leaflet.css"; import { Vehicle } from "@/types/types"; import { FitBoundsToVehicles } from "./focus-vehicle"; -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; +import { getCompaniesApi } from "@/api/companies"; + +interface Company { + id: number; + name: string; + address: string; + email: string; + phoneNumber: string; + industryType: string; + picName: string; + picPhone: string; + isActive: boolean; +} interface VehicleMapProps { vehicleLocations: Vehicle[]; @@ -143,13 +156,25 @@ const formatVehicleType = (type: string | undefined) => { const VehicleMap: React.FC = ({ vehicleLocations }) => { - // Ambil daftar unik perusahaan dari data kendaraan - const companyList = Array.from( - new Set(vehicleLocations.map((v) => v.company_name).filter(Boolean)) - ); - + const [companyList, setCompanyList] = useState([]); const [selectedCompany, setSelectedCompany] = useState(""); + // Fetch companies dari API + useEffect(() => { + const fetchCompanies = async () => { + try { + const companies: Company[] = await getCompaniesApi(); + const companyNames = companies.map((company) => company.name); + setCompanyList(companyNames); + } catch (error) { + console.error("Error fetching companies:", error); + setCompanyList([]); + } + }; + + fetchCompanies(); + }, []); + // Filter kendaraan berdasarkan perusahaan yang dipilih const filteredVehicles = selectedCompany && selectedCompany !== "" diff --git a/src/pages/kendaraan/dialog-kendaraan-detail.tsx b/src/pages/kendaraan/dialog-kendaraan-detail.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/pages/kendaraan/dialog-kendaraan-edit.tsx b/src/pages/kendaraan/dialog-kendaraan-edit.tsx new file mode 100644 index 0000000..6759720 --- /dev/null +++ b/src/pages/kendaraan/dialog-kendaraan-edit.tsx @@ -0,0 +1,505 @@ +"use client"; + +import { useState, useEffect, useCallback } from "react"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { getVehicleDetail } from "@/api/vehicle"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Textarea } from "@/components/ui/textarea"; + +interface Vehicle { + id: number; + companyId: number; + licensePlate: string; + description: string | null; + image: string; + vehicleType: string; + fuelTank: number; + frameNumber: string; + engineNumber: string; + color: string; + year: number; + brand: string; + model: string; + lastOdometer: number; + markingNumber: string; +} + +interface DialogKendaraanEditProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onSubmit?: (data: KendaraanFormData) => void; + vehicle?: Vehicle | null; + companyId?: number; +} + +interface KendaraanFormData { + vehicleId?: number; // Tambahkan vehicleId + licensePlate: string; + description: string; + vehicleType: string; + odometer: string; + tankCapacity: string; + frameNumber: string; + engineNumber: string; + color: string; + year: string; + brand: string; + model: string; + markingNumber: string; + hasFuel: boolean; + hasOnOff: boolean; + fuelCalibration: string; + imei?: string; + simNumber?: string; +} + +const initialFormData: KendaraanFormData = { + licensePlate: "", + description: "", + vehicleType: "", + odometer: "", + tankCapacity: "", + frameNumber: "", + engineNumber: "", + color: "", + year: "", + brand: "", + model: "", + markingNumber: "", + hasFuel: false, + hasOnOff: false, + fuelCalibration: "", + imei: "", + simNumber: "", +}; + +export function DialogKendaraanEdit({ + open, + onOpenChange, + onSubmit, + vehicle, + companyId, +}: DialogKendaraanEditProps) { + const [formData, setFormData] = useState(initialFormData); + const [loadingVehicle, setLoadingVehicle] = useState(false); + + const fetchVehicleData = useCallback(async (vehicleId: number, compId: number) => { + if (!vehicleId || !compId) { + console.log("No vehicleId or companyId provided"); + return; + } + + setLoadingVehicle(true); + console.log("Fetching vehicle data for vehicleId:", vehicleId, "companyId:", compId); + + try { + const vehicleData = await getVehicleDetail(vehicleId, compId); + console.log("Vehicle data received:", vehicleData); + + if (vehicleData) { + // Extract device info dari vehicleData + const imei = vehicleData.imei || ""; + const simNumber = vehicleData.simNumber || ""; + + console.log("IMEI:", imei); + console.log("SIM Number:", simNumber); + + // Map semua data dari API ke form data + setFormData({ + vehicleId: vehicleId, + licensePlate: vehicleData.licensePlate || "", + description: vehicleData.description || "", + vehicleType: vehicleData.vehicleType || "", + odometer: vehicleData.lastOdometer?.toString() || "", + tankCapacity: vehicleData.fuelTank?.toString() || "", + frameNumber: vehicleData.frameNumber || "", + engineNumber: vehicleData.engineNumber || "", + color: vehicleData.color || "", + year: vehicleData.year?.toString() || "", + brand: vehicleData.brand || "", + model: vehicleData.model || "", + markingNumber: vehicleData.markingNumber || "", + hasFuel: !!vehicleData.imei, + hasOnOff: false, + fuelCalibration: "", + imei: imei, + simNumber: simNumber, + }); + } else { + console.log("No vehicle data found"); + setFormData(initialFormData); + } + } catch (error) { + console.error("Error fetching vehicle:", error); + // Set ke initial data jika error + setFormData(initialFormData); + } finally { + setLoadingVehicle(false); + } + }, []); + + // Load initial data and nge fetch vehicle detail ketika dialog open + useEffect(() => { + if (open && vehicle && companyId) { + // Fetch full vehicle data menggunakan vehicle.id + if (vehicle.id) { + fetchVehicleData(vehicle.id, companyId); + } else { + setFormData(initialFormData); + } + } else if (!open) { + setFormData(initialFormData); + } + }, [open, vehicle, companyId, fetchVehicleData]); + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleSelectChange = (name: string, value: string) => { + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleCheckboxChange = (name: string, checked: boolean) => { + setFormData((prev) => ({ + ...prev, + [name]: checked, + })); + }; + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + onSubmit?.(formData); + setFormData(initialFormData); + onOpenChange(false); + }; + + const handleCancel = () => { + setFormData(initialFormData); + onOpenChange(false); + }; + + return ( + + + + + Edit Kendaraan + + + +
+ {/* General Info Section */} +
+

+ General Info +

+
+
+ + +
+ +
+ +