From 4cc01babf1cacc991be7011fdaa0c01b582e5ba1 Mon Sep 17 00:00:00 2001 From: vergiLgood1 Date: Wed, 7 May 2025 07:21:38 +0700 Subject: [PATCH] Enhance pop-up components with connection indicators and improve styling - Refactored IncidentPopup, TimelinePopup, and UnitPopup components to include connection lines and dots for better visual indication of their relation to the map. - Updated the layout and styling of the pop-ups for improved readability and consistency. - Adjusted the TIME_ZONES data to reflect more accurate geographical locations. - Enhanced the digital clock display in the TimeZonesDisplay component with improved styling and shadow effects. - Added new CSS styles for digital clock presentation to enhance user experience. --- .../_components/map/pop-up/crime-popup.tsx | 180 ++++---- .../_components/map/pop-up/district-popup.tsx | 430 +++++++++--------- .../_components/map/pop-up/incident-popup.tsx | 210 +++++---- .../_components/map/pop-up/timeline-popup.tsx | 121 +++-- .../app/_components/map/pop-up/unit-popup.tsx | 226 +++++---- .../app/_components/map/timezone.tsx | 25 +- sigap-website/app/_styles/globals.css | 13 + 7 files changed, 669 insertions(+), 536 deletions(-) diff --git a/sigap-website/app/_components/map/pop-up/crime-popup.tsx b/sigap-website/app/_components/map/pop-up/crime-popup.tsx index 042e95a..90f034c 100644 --- a/sigap-website/app/_components/map/pop-up/crime-popup.tsx +++ b/sigap-website/app/_components/map/pop-up/crime-popup.tsx @@ -82,101 +82,125 @@ export default function IncidentPopup({ longitude, latitude, onClose, incident } maxWidth="320px" className="incident-popup z-50" > - -
- {/* Custom close button */} - +
+ +
+ {/* Custom close button */} + -
-

- - {incident.category || "Unknown Incident"} -

- {getStatusBadge(incident.status)} -
- - {incident.description && ( -
-

- - {incident.description} -

+
+

+ + {incident.category || "Unknown Incident"} +

+ {getStatusBadge(incident.status)}
- )} - - - {/* Improved section headers */} -
- {incident.district && ( -
-

District

-

- - {incident.district} + {incident.description && ( +

+

+ + {incident.description}

)} - {incident.address && ( -
-

Location

-

- - {incident.address} -

-
- )} + - {incident.timestamp && ( - <> -
-

Date

+ {/* Improved section headers */} +
+ {incident.district && ( +
+

District

- - {formatDate(incident.timestamp)} + + {incident.district}

-
-

Time

+ )} + + {incident.address && ( +
+

Location

- - {formatTime(incident.timestamp)} + + {incident.address}

- - )} + )} - {incident.type_category && ( -
-

Type

-

- - {incident.type_category} -

-
- )} -
+ {incident.timestamp && ( + <> +
+

Date

+

+ + {formatDate(incident.timestamp)} +

+
+
+

Time

+

+ + {formatTime(incident.timestamp)} +

+
+ + )} -
-

- - Coordinates: {latitude.toFixed(6)}, {longitude.toFixed(6)} -

-

ID: {incident.id}

+ {incident.type_category && ( +
+

Type

+

+ + {incident.type_category} +

+
+ )} +
+ +
+

+ + Coordinates: {latitude.toFixed(6)}, {longitude.toFixed(6)} +

+

ID: {incident.id}

+
-
- + + {/* Connection line */} +
+ {/* Connection dot */} +
+
) } diff --git a/sigap-website/app/_components/map/pop-up/district-popup.tsx b/sigap-website/app/_components/map/pop-up/district-popup.tsx index 2de1015..dbca8a0 100644 --- a/sigap-website/app/_components/map/pop-up/district-popup.tsx +++ b/sigap-website/app/_components/map/pop-up/district-popup.tsx @@ -108,235 +108,253 @@ export default function DistrictPopup({ maxWidth="300px" className="district-popup z-50" > - -
- {/* Custom close button */} - - -
-
- -

{district.name}

-
- {getCrimeRateBadge(district.level)} -
- {/*
- - {getTimePeriod()} -
*/} -
- -
-
- - {formatNumber(district.number_of_crime || 0)} - Incidents -
- -
- - {formatNumber(district.demographics?.population || 0)} - Population -
- -
- - {formatNumber(district.geographics?.land_area || 0)} - km² -
-
- - - {/* Improved tab headers */} - - + +
+ {/* Custom close button */} + - {/* Tab content with improved section headers */} - -
-
-
- -
-
-

Crime Level

-

- This area has a {district.level || "unknown"} level of crime based on incident reports. -

-
-
- - {district.geographics && district.geographics.land_area && ( -
-
- -
-
-

Geography

-

- Land area: {formatNumber(district.geographics.land_area)} km² -

- {district.geographics.address && ( -

Address: {district.geographics.address}

- )} -
-
- )} - -
-
- -
-
-

Time Period

-

- Data shown for {getTimePeriod()} - {filterCategory !== "all" ? ` (${filterCategory} category)` : ""} -

-
+
+
+ +

{district.name}

+ {getCrimeRateBadge(district.level)}
- +
- - {district.demographics ? ( +
+
+ + {formatNumber(district.number_of_crime || 0)} + Incidents +
+ +
+ + {formatNumber(district.demographics?.population || 0)} + Population +
+ +
+ + {formatNumber(district.geographics?.land_area || 0)} + km² +
+
+ + + + + Overview + + + Demographics + + + Incidents + + + +
-
- +
+
-

Population

+

Crime Level

- Total: {formatNumber(district.demographics.population || 0)} -

-

- Density: {formatNumber(district.demographics.population_density || 0)} people/km² + This area has a {district.level || "unknown"} level of crime based on incident reports.

-
-
- -
-
-

Unemployment

-

- {formatNumber(district.demographics.number_of_unemployed || 0)} unemployed people -

- {district.demographics.population && district.demographics.number_of_unemployed && ( -

- Rate:{" "} - {( - (district.demographics.number_of_unemployed / district.demographics.population) * - 100 - ).toFixed(1)} - % -

- )} -
-
- -
-
- -
-
-

Crime Rate

- {district.number_of_crime && district.demographics.population ? ( -

- {((district.number_of_crime / district.demographics.population) * 10000).toFixed(2)} crime - incidents per 10,000 people -

- ) : ( -

No data available

- )} -
-
-
- ) : ( -
- -

No demographic data available for this district.

-
- )} - - - - {allCrimeIncidents && allCrimeIncidents.length > 0 ? ( -
- {allCrimeIncidents.map((incident, index) => ( -
-
- - - {incident.category || incident.type || "Unknown"} - - - {incident.status || "unknown"} - + {district.geographics && district.geographics.land_area && ( +
+
+
-

{incident.description || "No description"}

-
-

- {incident.timestamp ? new Date(incident.timestamp).toLocaleString() : "Unknown date"} +

+

Geography

+

+ Land area: {formatNumber(district.geographics.land_area)} km²

- + {district.geographics.address && ( +

Address: {district.geographics.address}

+ )}
- ))} - - {district.number_of_crime > allCrimeIncidents.length && ( -
-

- Showing {allCrimeIncidents.length} of {district.number_of_crime} total incidents - {filterCategory !== "all" ? ` for ${filterCategory} category` : ""} -

-
)} + +
+
+ +
+
+

Time Period

+

+ Data shown for {getTimePeriod()} + {filterCategory !== "all" ? ` (${filterCategory} category)` : ""} +

+
+
- ) : ( -
- + + + + {district.demographics ? ( +
+
+
+ +
+
+

Population

+

+ Total: {formatNumber(district.demographics.population || 0)} +

+

+ Density: {formatNumber(district.demographics.population_density || 0)} people/km² +

+
+
+ +
+
+ +
+
+

Unemployment

+

+ {formatNumber(district.demographics.number_of_unemployed || 0)} unemployed people +

+ {district.demographics.population && district.demographics.number_of_unemployed && ( +

+ Rate:{" "} + {( + (district.demographics.number_of_unemployed / district.demographics.population) * + 100 + ).toFixed(1)} + % +

+ )} +
+
+ +
+
+ +
+
+

Crime Rate

+ {district.number_of_crime && district.demographics.population ? ( +

+ {((district.number_of_crime / district.demographics.population) * 10000).toFixed(2)} crime + incidents per 10,000 people +

+ ) : ( +

No data available

+ )} +
+
+
+ ) : ( +
+ +

No demographic data available for this district.

+
+ )} +
+ + + {allCrimeIncidents && allCrimeIncidents.length > 0 ? ( +
+ {allCrimeIncidents.map((incident, index) => ( +
+
+ + + {incident.category || incident.type || "Unknown"} + + + {incident.status || "unknown"} + +
+

{incident.description || "No description"}

+
+

+ {incident.timestamp ? new Date(incident.timestamp).toLocaleString() : "Unknown date"} +

+ +
+
+ ))} + + {district.number_of_crime > allCrimeIncidents.length && ( +
+

+ Showing {allCrimeIncidents.length} of {district.number_of_crime} total incidents + {filterCategory !== "all" ? ` for ${filterCategory} category` : ""} +

+
+ )} +
+ ) : ( +
+

No crime incidents available to display{filterCategory !== "all" ? ` for ${filterCategory}` : ""}.

-

Total reported incidents: {district.number_of_crime || 0}

-
- )} -
- - +

Total reported incidents: {district.number_of_crime || 0}

+
+ )} + + + + {/* Connection line */} +
+ {/* Connection dot */} +
+
) } diff --git a/sigap-website/app/_components/map/pop-up/incident-popup.tsx b/sigap-website/app/_components/map/pop-up/incident-popup.tsx index e12caf0..78d1fbd 100644 --- a/sigap-website/app/_components/map/pop-up/incident-popup.tsx +++ b/sigap-website/app/_components/map/pop-up/incident-popup.tsx @@ -65,116 +65,140 @@ export default function IncidentPopup({ maxWidth="320px" className="incident-popup z-50" > - -
- {/* Custom close button */} - +
+ +
+ {/* Custom close button */} + -
-

- - {incident.category || "Unknown Incident"} -

-
- - {incident.description && ( -
-

- - {incident.description} -

+
+

+ + {incident.category || "Unknown Incident"} +

- )} - - -
- {incident.district && ( -
-

District

-

- - {incident.district} + {incident.description && ( +

+

+ + {incident.description}

)} - {incident.date && ( - <> -
-

Date

+ + +
+ {incident.district && ( +
+

District

- - {formatDate(incident.date)} + + {incident.district}

-
-

Time

-

- - {formatTime(incident.date)} -

-
- - )} -
+ )} - {/* Distances to police units section */} - + {incident.date && ( + <> +
+

Date

+

+ + {formatDate(incident.date)} +

+
+
+

Time

+

+ + {formatTime(incident.date)} +

+
+ + )} +
-
-

Nearby Police Units

+ {/* Distances to police units section */} + - {isLoadingDistances ? ( -
- - - -
- ) : distances.length > 0 ? ( - +
+

Nearby Police Units

+ + {isLoadingDistances ? (
- {distances.map((item) => ( -
-
-

{item.unit_name || "Unknown Unit"}

-

- {item.unit_type || "Police Unit"} -

-
- - {formatDistance(item.distance_meters)} - -
- ))} + + +
- - ) : ( -

- No police units data available -

- )} -
+ ) : distances.length > 0 ? ( + +
+ {distances.map((item) => ( +
+
+

{item.unit_name || "Unknown Unit"}

+

+ {item.unit_type || "Police Unit"} +

+
+ + {formatDistance(item.distance_meters)} + +
+ ))} +
+
+ ) : ( +

+ No police units data available +

+ )} +
-
-

- - Coordinates: {latitude.toFixed(6)}, {longitude.toFixed(6)} -

-

ID: {incident.id}

+
+

+ + Coordinates: {latitude.toFixed(6)}, {longitude.toFixed(6)} +

+

ID: {incident.id}

+
-
- + + {/* Connection line */} +
+ {/* Connection dot */} +
+
) } diff --git a/sigap-website/app/_components/map/pop-up/timeline-popup.tsx b/sigap-website/app/_components/map/pop-up/timeline-popup.tsx index d2f4003..581e358 100644 --- a/sigap-website/app/_components/map/pop-up/timeline-popup.tsx +++ b/sigap-website/app/_components/map/pop-up/timeline-popup.tsx @@ -7,7 +7,6 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../.. import { Button } from "../../ui/button" import { Badge } from "../../ui/badge" - interface TimelinePopupProps { longitude: number latitude: number @@ -63,60 +62,84 @@ export default function TimelinePopup({ className="z-10" maxWidth="300px" > - - -
- {district.name} - -
- - Average incident time analysis - -
- -
-
-
{district.formattedTime}
- - {district.timeDescription} - +
+ + +
+ {district.name} +
-
- Based on {district.totalIncidents} incidents + + Average incident time analysis + + + +
+
+
{district.formattedTime}
+ + {district.timeDescription} + +
+
+ Based on {district.totalIncidents} incidents +
-
-
-
- Earliest incident: - {district.earliestTime} +
+
+ Earliest incident: + {district.earliestTime} +
+
+ Latest incident: + {district.latestTime} +
-
- Latest incident: - {district.latestTime} -
-
-
-
Top incident types:
-
- {topCategories.map(([category, count]) => ( -
- {category} - {count} -
- ))} +
+
Top incident types:
+
+ {topCategories.map(([category, count]) => ( +
+ {category} + {count} +
+ ))} +
-
- - + + + {/* Connection line */} +
+ {/* Connection dot */} +
+
) } diff --git a/sigap-website/app/_components/map/pop-up/unit-popup.tsx b/sigap-website/app/_components/map/pop-up/unit-popup.tsx index 58297ad..83ab2b5 100644 --- a/sigap-website/app/_components/map/pop-up/unit-popup.tsx +++ b/sigap-website/app/_components/map/pop-up/unit-popup.tsx @@ -56,112 +56,136 @@ export default function UnitPopup({ maxWidth="320px" className="unit-popup z-50" > - -
- {/* Custom close button */} - +
+ +
+ {/* Custom close button */} + -
-

- - {unit.name || "Police Unit"} -

- - {unit.type || "Unit"} - -
+
+

+ + {unit.name || "Police Unit"} +

+ + {unit.type || "Unit"} + +
-
- {unit.address && ( -
-

Address

-

- - {unit.address} -

-
- )} - - {unit.phone && ( -
-

Contact

-

- - {unit.phone} -

-
- )} - - {unit.district && ( -
-

District

-

- - {unit.district} -

-
- )} -
- - {/* Distances to incidents section */} - - -
-

- - Nearby Incidents -

- - {isLoadingDistances ? ( -
- - - -
- ) : distances.length > 0 ? ( - -
- {distances.map((item) => ( -
-
-

{item.category_name || "Unknown"}

-

- {item.incident_description || "No description"} -

-
- - {formatDistance(item.distance_meters)} - -
- ))} +
+ {unit.address && ( +
+

Address

+

+ + {unit.address} +

- - ) : ( -

- No incident data available -

- )} -
+ )} -
-

- - Coordinates: {latitude.toFixed(6)}, {longitude.toFixed(6)} -

-

ID: {unit.id}

+ {unit.phone && ( +
+

Contact

+

+ + {unit.phone} +

+
+ )} + + {unit.district && ( +
+

District

+

+ + {unit.district} +

+
+ )} +
+ + {/* Distances to incidents section */} + + +
+

+ + Nearby Incidents +

+ + {isLoadingDistances ? ( +
+ + + +
+ ) : distances.length > 0 ? ( + +
+ {distances.map((item) => ( +
+
+

{item.category_name || "Unknown"}

+

+ {item.incident_description || "No description"} +

+
+ + {formatDistance(item.distance_meters)} + +
+ ))} +
+
+ ) : ( +

+ No incident data available +

+ )} +
+ +
+

+ + Coordinates: {latitude.toFixed(6)}, {longitude.toFixed(6)} +

+

ID: {unit.id}

+
-
- + + {/* Connection line */} +
+ {/* Connection dot */} +
+
) } diff --git a/sigap-website/app/_components/map/timezone.tsx b/sigap-website/app/_components/map/timezone.tsx index fa77913..51c2bb6 100644 --- a/sigap-website/app/_components/map/timezone.tsx +++ b/sigap-website/app/_components/map/timezone.tsx @@ -11,9 +11,9 @@ interface TimeZoneMarker { } const TIME_ZONES: TimeZoneMarker[] = [ - { name: "WIB", offset: 7, longitude: 106.8456, latitude: -6.2088 }, // Jakarta - { name: "WITA", offset: 8, longitude: 115.1889, latitude: -8.4095 }, // Denpasar - { name: "WIT", offset: 9, longitude: 140.7887, latitude: -2.5916 }, // Jayapura + { name: "WIB", offset: 7, longitude: 110.5, latitude: -3.3 }, // Between Java and Sumatra + { name: "WITA", offset: 8, longitude: 118.8, latitude: -2.5 }, // Between Java and Kalimantan + { name: "WIT", offset: 9, longitude: 128.0, latitude: -2.0 }, // Between Sulawesi and Papua ] export default function TimeZonesDisplay() { @@ -49,12 +49,19 @@ export default function TimeZonesDisplay() { <> {TIME_ZONES.map((zone) => ( -
-
-
-
{zone.name}
-
{currentTimes[zone.name] || "00:00:00"}
-
GMT+{zone.offset}
+
+
+
+
{zone.name} / GMT+{zone.offset}
+
+ {currentTimes[zone.name] || "00:00:00"} +
diff --git a/sigap-website/app/_styles/globals.css b/sigap-website/app/_styles/globals.css index c682710..75216f9 100644 --- a/sigap-website/app/_styles/globals.css +++ b/sigap-website/app/_styles/globals.css @@ -217,3 +217,16 @@ text-align: center; color: #ccc; } + +/* Digital Clock Styling */ +.digital-clock { + font-variant-numeric: tabular-nums; + letter-spacing: 0.05em; + background-color: rgba(0, 0, 0, 0.7); + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5); + display: inline-block; + margin: 0.25rem 0; +} +