diff --git a/src/App.tsx b/src/App.tsx index 146e516..7e30b3a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,9 @@ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + refetchOnMount: false, }, }, }); diff --git a/src/components/ui/bottom-navigation/bottom-navigation.tsx b/src/components/ui/bottom-navigation/bottom-navigation.tsx index 13a72d1..da41f47 100644 --- a/src/components/ui/bottom-navigation/bottom-navigation.tsx +++ b/src/components/ui/bottom-navigation/bottom-navigation.tsx @@ -11,10 +11,10 @@ export default function BottomNavigation() { console.log(location.pathname) const tabs = [ - { path: "/", icon: Home, label: "Dashboard" }, - { path: "/analysis", icon: BarChart3, label: "analysis" }, - { path: "/rooms", icon: DoorOpen, label: "Rooms" }, - { path: "/settings", icon: Settings, label: "Settings" }, + { path: "/", icon: Home, label: "Dashboard", disabled: false }, + { path: "/analysis", icon: BarChart3, label: "analysis", disabled: true }, + { path: "/rooms", icon: DoorOpen, label: "Rooms", disabled: true }, + { path: "/settings", icon: Settings, label: "Settings", disabled: false }, ]; return ( @@ -24,7 +24,7 @@ console.log(location.pathname) {tab.label} diff --git a/src/features/device/add/index.tsx b/src/features/device/add/index.tsx index 33432cf..3b0b8fe 100644 --- a/src/features/device/add/index.tsx +++ b/src/features/device/add/index.tsx @@ -2,27 +2,30 @@ import { useState } from "react"; import { getDevices, saveDevices, - isDuplicateDevice, + getMerchant, } from "../../../utils/storage"; import { useNavigate } from "@tanstack/react-router"; -const initialState: DeviceData = { - towerNumber: "", - floorName: "", - unitNumber: "", - roomName: "", - deviceName: "", - deviceType: "", - code: "", - payload: "", - active: true, - status: false, -}; + export default function AddDeviceFeature() { - const [form, setForm] = useState(initialState); + const [form, setForm] = useState(null); const navigate = useNavigate(); + const merchant = getMerchant(); + + const isMerchantValid = + merchant?.merchantName && + merchant?.tower && + merchant?.floor && + merchant?.unit; + + const isFormValid = + form?.roomName?.trim() && + form?.deviceName?.trim() && + form?.deviceType?.trim() && + form?.deviceLabel?.trim(); + function handleChange( e: React.ChangeEvent ) { @@ -32,116 +35,104 @@ export default function AddDeviceFeature() { function handleSubmit(e: React.FormEvent) { e.preventDefault(); - + if (!isMerchantValid || !isFormValid) return; + const devices = getDevices(); - + const deviceToSave: DeviceData = { ...form, - deviceLabel: form.deviceLabel || `${form.roomName} ${form.deviceName}`, + towerNumber: merchant.tower, + floorName: merchant.floor, + unitNumber: merchant.unit, + deviceLabel: form?.deviceLabel, }; - - if (isDuplicateDevice(devices, deviceToSave)) { - return; - } - + saveDevices([...devices, deviceToSave]); - setForm(initialState); - navigate({ to: '/' }); + setForm(null); + navigate({ to: "/" }); } return (

Add Device

+ + {!isMerchantValid && ( +

+ Lengkapi data merchant (tower, floor, unit) terlebih dahulu +

+ )} +
-
- - -
-
- - -
+ {/* ROOM */}
-
-
- -
+ + {/* DEVICE NAME */}
+ + {/* DEVICE TYPE */}
+ + {/* DEVICE LABEL */}
diff --git a/src/features/home/hooks/queries.ts b/src/features/home/hooks/queries.ts index 39d3a00..e8bef2a 100644 --- a/src/features/home/hooks/queries.ts +++ b/src/features/home/hooks/queries.ts @@ -1,32 +1,36 @@ import { useQueries } from "@tanstack/react-query"; import { getDeviceStatus } from "../../../repositories/device"; -import { DEVICES_DATA } from "../../../utils/data"; import { getDevices, getMerchant } from "../../../utils/storage"; export function useDevices() { + const merchant = getMerchant(); const dataStorage = getDevices(); - const datas = [...DEVICES_DATA, ...dataStorage]; - + const datas = [...dataStorage]; + const queries = useQueries({ queries: datas.map((device) => ({ queryKey: [ "device-status", - device.code, + merchant?.merchantName, + device.towerNumber, + device.floorName, + device.unitNumber, + device.deviceName, device.roomName, device.deviceType, + "S", ], queryFn: async () => { - const merchant = getMerchant(); const params = { - merchantName: merchant, + merchantName: merchant?.merchantName, + towerNumber: device.towerNumber, floorName: device.floorName, unitNumber: device.unitNumber, deviceName: device.deviceName, roomName: device.roomName, deviceType: device.deviceType, commandType: "S", - towerNumber: device.towerNumber, }; const data = await getDeviceStatus(params); @@ -34,28 +38,28 @@ export function useDevices() { ...device, code: data.data.code, deviceName: device.deviceName, + payload: data.data.payload, status: data.data.payload?.toLowerCase().includes("on") || data.data.payload?.toLowerCase().includes("lock") || - data.data.payload?.toLowerCase().includes("Open") + data.data.payload?.toLowerCase().includes("open") ? true : false, } as unknown as Device; }, - staleTime: 10_000, - retry: 1, })), }); - + const devices = queries - .map((q, index) => ({ - ...q.data, - isLoading: q.isLoading || q.isFetching, - isError: q.isError, - refetch: q.refetch, // 👈 per device - key: datas[index].code, - })) - .filter((d) => !d?.isError); + .map((q, index) => ({ + ...q.data, + isLoading: q.isLoading || q.isFetching, + isError: q.isError, + refetch: q.refetch, // 👈 per device + key: datas[index].code, + })) + .filter((d) => !d?.isError); + console.log(devices); return { data: devices as unknown as DeviceData[], diff --git a/src/features/home/sections/devices.tsx b/src/features/home/sections/devices.tsx index 214d51c..6f8b73a 100644 --- a/src/features/home/sections/devices.tsx +++ b/src/features/home/sections/devices.tsx @@ -1,9 +1,19 @@ -import { Lightbulb, Minus, Plus, SpeakerIcon, Tv, Wind } from "lucide-react"; +import { + Blinds, + ChefHat, + DoorClosed, + Fan, + Lightbulb, + Minus, + Plus, + Wind, + WindArrowDown, +} from "lucide-react"; import { Switch } from "../../../components/ui/switch"; import { useNavigate } from "@tanstack/react-router"; import Card from "../../../components/ui/card"; import { useDevices } from "../hooks/queries"; -import { getMerchant } from "../../../utils/storage"; +import { getMerchant } from "../../../utils/storage"; import { useDeviceCommand } from "../hooks/mutations"; export default function Devices() { @@ -15,17 +25,16 @@ export default function Devices() { const { isPending, mutate } = useDeviceCommand(); const onSubmit = (payload: DeviceData, action = "") => { - mutate( { + towerNumber: payload.towerNumber, + merchantName: merchant?.merchantName, commandType: "C", deviceName: payload.deviceName, deviceType: payload.deviceType, floorName: payload.floorName, - merchantName: merchant, payload: { action }, roomName: payload.roomName, - towerNumber: payload.towerNumber, unitNumber: payload.unitNumber, }, { @@ -35,20 +44,20 @@ export default function Devices() { onError: () => { payload?.refetch?.(); }, - } + }, ); }; - const icons = [ - { icon: , label: "light" }, - { icon: , label: "tv" }, - { icon: , label: "power" }, - { icon: , label: "ac" }, - { icon: , label: "device" }, - ]; - - - // console.log(data); + const icons = { + L: { icon: , label: "Lampu" }, + BL: { icon: , label: "Blind" }, + AC: { icon: , label: "AC" }, + DL: { icon: , label: "Doorlock" }, + ERV: { icon: , label: "ERV" }, + PM25: { icon: , label: "Sensor udara" }, + KC: { icon: , label: "Kitchen Appliances" }, + FT1: { icon: , label: "IAQ" }, + }; return ( <> @@ -56,7 +65,7 @@ export default function Devices() {

Your Devices

-
+
- {icons.map((item, idx) => ( + {/* {icons.map((item, idx) => ( - ))} + ))} */}
+ {isLoading && ( + <> + {[...Array(4)].map((item) => ( + + {/* Header */} +
+
+
+
+ + {/* Title */} +
+ + {/* Device */} +
+ + {/* Status */} +
+ + {/* Location */} +
+ + ))} + + )} {!isLoading && data?.map((item, index) => (
-
- icon +
+ {icons[item.deviceName as keyof typeof icons]?.icon}
- {!["FT1"].includes(item.deviceName) && ( + {!["FT1"].includes(item.deviceName ?? "") && ( -

+

{item?.deviceLabel}

+

+ Device:{" "} + {icons[item.deviceName as keyof typeof icons]?.label ?? + item.deviceName} +

+

+ Status: {item?.payload?.toString()} +

- {item?.towerNumber}/{item?.floorName}/{item?.roomName} + {item?.towerNumber}/{item?.floorName}/{item?.unitNumber}/ + {item?.roomName}

{item?.deviceName === "AC" && ( diff --git a/src/features/settings/index.tsx b/src/features/settings/index.tsx index e04260f..008b97d 100644 --- a/src/features/settings/index.tsx +++ b/src/features/settings/index.tsx @@ -4,32 +4,56 @@ import { saveMerchant, getMerchant, } from "../../utils/storage"; +import { useNavigate } from "@tanstack/react-router"; + export default function SettingsFeature() { - const [form, setForm] = useState(''); + const navigate = useNavigate(); + + const [form, setForm] = useState({ + merchantName: "", + tower: "", + floor: "", + unit: "", + }); const [loading, setLoading] = useState(false); - const merchant = getMerchant(); + function handleChange( - e: React.ChangeEvent + e: React.ChangeEvent ) { - const { value } = e.target; - setForm(value); + const { name, value } = e.target; + setForm((prev) => ({ + ...prev, + [name]: value, + })); } function handleSubmit(e: React.FormEvent) { e.preventDefault(); + if (!isFormValid) return; + setLoading(true); - saveMerchant(form) + saveMerchant(form); + setTimeout(() => { setLoading(false); + navigate({ to: "/" }); }, 200); - } useEffect(() => { - setForm(merchant || 'SAVY'); - },[merchant]); + const merchant = getMerchant(); + if (merchant) { + setForm(merchant); + } + }, []); + + const isFormValid = + form.merchantName?.trim() && + form.tower?.trim() && + form.floor?.trim() && + form.unit?.trim(); return ( @@ -40,21 +64,62 @@ export default function SettingsFeature() {
- + +
+ + +
+ +
+
+ + +
+ +
+ + +
+
); diff --git a/src/repositories/device/types.d.ts b/src/repositories/device/types.d.ts index 56a0c52..32e9297 100644 --- a/src/repositories/device/types.d.ts +++ b/src/repositories/device/types.d.ts @@ -5,13 +5,13 @@ type Device = { }; type DeviceData = { - floorName: string; - unitNumber: string; - deviceName: string; - roomName: string; - deviceType: string; - code: string; - towerNumber: string; + floorName?: string; + unitNumber?: string; + deviceName?: string; + roomName?: string; + deviceType?: string; + code?: string; + towerNumber?: string; payload?: string; deviceLabel?: string deviceName?: string @@ -21,26 +21,26 @@ type DeviceData = { }; type DeviceParams = { - merchantName: string; - floorName: string; - unitNumber: string; - deviceName: string; - roomName: string; - deviceType: string; - commandType: string; - towerNumber: string; + merchantName?: string; + floorName?: string; + unitNumber?: string; + deviceName?: string; + roomName?: string; + deviceType?: string; + commandType?: string; + towerNumber?: string; }; type DevicePayload = { - commandType: string, - deviceName: string, - deviceType: string, - floorName: string, - merchantName: string, - payload: { - action: string + commandType?: string, + deviceName?: string, + deviceType?: string, + floorName?: string, + merchantName?: string, + payload?: { + action?: string }, - roomName: string, - towerNumber: string, - unitNumber: string, + roomName?: string, + towerNumber?: string, + unitNumber?: string, } \ No newline at end of file diff --git a/src/types/merchant.d.ts b/src/types/merchant.d.ts new file mode 100644 index 0000000..7fcb9d0 --- /dev/null +++ b/src/types/merchant.d.ts @@ -0,0 +1,6 @@ +type MerchantForm = { + merchantName: string; + tower: string; + floor: string; + unit: string; +}; diff --git a/src/utils/storage.ts b/src/utils/storage.ts index dcbece8..c9dd727 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -28,14 +28,14 @@ export function isDuplicateDevice( } -export function getMerchant(): string { +export function getMerchant(): MerchantForm | null { try { return JSON.parse(localStorage.getItem(MERCHANT_KEY) || ""); } catch { - return ''; + return null; } } -export function saveMerchant(val: string) { +export function saveMerchant(val: MerchantForm) { localStorage.setItem(MERCHANT_KEY, JSON.stringify(val)); }