diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..edf889d --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +# API base for the browser → Django. +# Dev: http://localhost:8000/ (docker compose maps host :8000 → backend:5000) +# Prod: leave empty for same-origin (nginx proxies /api, /callback, /auth to Django) +VITE_API_URL=http://localhost:8000/ diff --git a/index.html b/index.html index 43e0a27..c1d3e34 100644 --- a/index.html +++ b/index.html @@ -1,12 +1,19 @@ - +
- + -
- Edit src/App.tsx and save to test HMR
-
Account No
+ {accountNo} + +{user?.id_card}
+{user?.mobile}
+
+ + Pay for your devices and track your bills. +
+Device Inactive
+ )} + {device.has_a_pending_payment && ( + + + Payment Pending{" "} +{device?.reason_for_blocking}
+Device Inactive
+ )} + {device.has_a_pending_payment && ( + + + Payment Pending{" "} +{device?.reason_for_blocking}
++ Device Name: {value} +
+ ); + case "mac": + return ( ++ MAC Address: {value} +
+ ); + case "vendor": + return ( ++ Vendor: {value} +
+ ); + default: + return filter; + } +} diff --git a/src/components/filter.tsx b/src/components/filter.tsx new file mode 100644 index 0000000..acec3e3 --- /dev/null +++ b/src/components/filter.tsx @@ -0,0 +1,70 @@ +import { useState, useTransition } from "react"; +import { useSearchParams } from "react-router-dom"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { cn } from "@/lib/utils"; + +interface FilterOption { + value: string; + label: string; + icon: React.ReactNode; +} + +interface FilterProps { + options: FilterOption[]; + defaultOption: string; + queryParamKey: string; +} + +export default function Filter({ + options, + defaultOption, + queryParamKey, +}: FilterProps) { + const [selectedOption, setSelectedOption] = useState(defaultOption); + const [searchParams, setSearchParams] = useSearchParams(); + const [isPending, startTransition] = useTransition(); + + function handleFilterChange(value: string) { + setSelectedOption(value); + const params = new URLSearchParams(searchParams); + + params.set(queryParamKey, value); + params.set("page", "1"); + + startTransition(() => { + setSearchParams(params, { replace: true }); + }); + } + + return ( ++ {config.label.replace(/%20/g, " ")}:{" "} + {labels} +
+ ); + } + if (config.type === "dual-range-slider") { + const rangeValues = value as number[]; + const formatLabel = + config.formatLabel || + ((val: number | undefined) => val?.toString() || ""); + return ( ++ {config.label}:{" "} + + {formatLabel(rangeValues[0])} - {formatLabel(rangeValues[1])} + +
+ ); + } + if (config.type === "radio-group") { + const stringValue = value as string; + // For true/false values, display the label instead of the value + if (stringValue === "true" || stringValue === "false") { + const option = config.options.find((opt) => opt.value === stringValue); + const displayValue = option?.label || stringValue; + return ( ++ {config.label}:{" "} + {displayValue} +
+ ); + } + // For other values, display as normal + return ( ++ {config.label}:{" "} + {stringValue} +
+ ); + } + return ( ++ {config.label}: {value} +
+ ); + }; + + // Handles removing an individual filter + const handleRemoveFilter = (keyToRemove: TFilterKeys) => { + const newParams = new URLSearchParams(currentParams.toString()); + + // Clear the specific input's local state + const inputConfig = inputs.find((input) => input.name === keyToRemove); + + if (inputConfig?.type === "dual-range-slider") { + newParams.delete(`${keyToRemove}_min`); + newParams.delete(`${keyToRemove}_max`); + setInputValues((prev) => ({ + ...prev, + [keyToRemove]: [inputConfig.min, inputConfig.max], + })); + } else { + newParams.delete(keyToRemove); + setInputValues((prev) => ({ + ...prev, + [keyToRemove]: inputConfig?.type === "checkbox-group" ? [] : "", + })); + } + + newParams.set("page", "1"); // Reset page after removing a filter + + startTransition(() => { + navigate(`${pathname}?${newParams.toString()}`, { replace: true }); + }); + }; + + return ( ++ A MAC (Media Access Control) address is a unique identifier assigned + to a device`'`s network. It is used to identify the device on a + network, helping to differentiate devices on a network. +
+Please contact SAR Link for assistance.
+ + + +
Connect with us
-Join the Vite community
---
-
-
- GitHub
-
-
- -
-
-
- Discord
-
-
- -
-
-
- X.com
-
-
- -
-
-
- Bluesky
-
-
-
-