Compare commits
7 Commits
fixed-maps
...
fix-maps/2
| Author | SHA1 | Date | |
|---|---|---|---|
| 67070bb2f1 | |||
| fb19ec60b2 | |||
| e8f5c5b174 | |||
| 74a4d88277 | |||
| 2ad93a26a8 | |||
| 768b0caa9e | |||
| 208b0ce813 |
@@ -77,7 +77,6 @@ export default {
|
||||
},
|
||||
],
|
||||
"expo-font",
|
||||
"@rnmapbox/maps",
|
||||
"@react-native-firebase/app",
|
||||
[
|
||||
"expo-notifications",
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import MapsView from "@/screens/Maps/MapsView";
|
||||
import MapsView2 from "@/screens/Maps/MapsView2";
|
||||
import { Text, View } from "react-native";
|
||||
|
||||
export interface LocationItem {
|
||||
id: string | number;
|
||||
@@ -13,8 +11,14 @@ export interface LocationItem {
|
||||
export default function Maps() {
|
||||
return (
|
||||
<>
|
||||
<MapsView />
|
||||
{/* <MapsView2 />, */}
|
||||
{/* <Stack.Screen
|
||||
options={{
|
||||
title: "Maps",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/> */}
|
||||
{/* {Platform.OS === "ios" ? <MapsView /> : <MapsView2 />} */}
|
||||
<MapsView2 />
|
||||
{/* <View style={{ flex: 1, backgroundColor: "gray" }}><Text style={{ color: "white" }}>Map disabled</Text></View> */}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ButtonCustom,
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ButtonCustom,
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
@@ -94,11 +94,14 @@ export default function Portofolio() {
|
||||
data={data}
|
||||
listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any[]}
|
||||
/>
|
||||
<Portofolio_BusinessLocation
|
||||
data={data?.BusinessMaps}
|
||||
imageId={data?.logoId}
|
||||
setOpenDrawerLocation={setOpenDrawerLocation}
|
||||
/>
|
||||
{data?.BusinessMaps && (
|
||||
<Portofolio_BusinessLocation
|
||||
data={data?.BusinessMaps}
|
||||
imageId={data?.logoId}
|
||||
setOpenDrawerLocation={setOpenDrawerLocation}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Portofolio_SocialMediaSection
|
||||
data={data?.Portofolio_MediaSosial}
|
||||
/>
|
||||
@@ -135,10 +138,12 @@ export default function Portofolio() {
|
||||
closeDrawer={() => setOpenDrawerLocation(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<DummyLandscapeImage
|
||||
height={200}
|
||||
imageId={data?.BusinessMaps?.imageId}
|
||||
/>
|
||||
{data?.BusinessMaps?.imageId && (
|
||||
<DummyLandscapeImage
|
||||
height={200}
|
||||
imageId={data?.BusinessMaps?.imageId}
|
||||
/>
|
||||
)}
|
||||
<Spacing />
|
||||
<StackCustom gap={"xs"}>
|
||||
<GridTwoView
|
||||
|
||||
69
bun.lock
69
bun.lock
@@ -15,7 +15,6 @@
|
||||
"@react-navigation/elements": "^2.3.8",
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/native-stack": "^7.3.10",
|
||||
"@rnmapbox/maps": "^10.2.7",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/react-native-vector-icons": "^6.4.18",
|
||||
"axios": "^1.11.0",
|
||||
@@ -745,8 +744,6 @@
|
||||
|
||||
"@react-navigation/routers": ["@react-navigation/routers@7.5.3", "", { "dependencies": { "nanoid": "^3.3.11" } }, "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg=="],
|
||||
|
||||
"@rnmapbox/maps": ["@rnmapbox/maps@10.2.10", "", { "dependencies": { "@turf/along": "6.5.0", "@turf/distance": "6.5.0", "@turf/helpers": "6.5.0", "@turf/length": "6.5.0", "@turf/nearest-point-on-line": "6.5.0", "@types/geojson": "^7946.0.7", "debounce": "^2.2.0" }, "peerDependencies": { "expo": ">=47.0.0", "mapbox-gl": "^2.9.0", "react": ">=17.0.0", "react-dom": ">= 17.0.0", "react-native": ">=0.69" }, "optionalPeers": ["expo", "mapbox-gl", "react-dom"] }, "sha512-OfjW0rHp5bUWfzBo5fZ7qdKwAzGoocXYTsSssSPVMxZ2Y7axuhcbmsO5bV6gg+BJs5RwEsghzwTIoGydBNUClA=="],
|
||||
|
||||
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
|
||||
|
||||
"@sideway/address": ["@sideway/address@4.1.5", "", { "dependencies": { "@hapi/hoek": "^9.0.0" } }, "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q=="],
|
||||
@@ -767,14 +764,6 @@
|
||||
|
||||
"@tsconfig/node18": ["@tsconfig/node18@18.2.6", "", {}, "sha512-eAWQzAjPj18tKnDzmWstz4OyWewLUNBm9tdoN9LayzoboRktYx3Enk1ZXPmThj55L7c4VWYq/Bzq0A51znZfhw=="],
|
||||
|
||||
"@turf/along": ["@turf/along@6.5.0", "", { "dependencies": { "@turf/bearing": "^6.5.0", "@turf/destination": "^6.5.0", "@turf/distance": "^6.5.0", "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" } }, "sha512-LLyWQ0AARqJCmMcIEAXF4GEu8usmd4Kbz3qk1Oy5HoRNpZX47+i5exQtmIWKdqJ1MMhW26fCTXgpsEs5zgJ5gw=="],
|
||||
|
||||
"@turf/bbox": ["@turf/bbox@7.3.4", "", { "dependencies": { "@turf/helpers": "7.3.4", "@turf/meta": "7.3.4", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-D5ErVWtfQbEPh11yzI69uxqrcJmbPU/9Y59f1uTapgwAwQHQztDWgsYpnL3ns8r1GmPWLP8sGJLVTIk2TZSiYA=="],
|
||||
|
||||
"@turf/bearing": ["@turf/bearing@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" } }, "sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A=="],
|
||||
|
||||
"@turf/destination": ["@turf/destination@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" } }, "sha512-4cnWQlNC8d1tItOz9B4pmJdWpXqS0vEvv65bI/Pj/genJnsL7evI0/Xw42RvEGROS481MPiU80xzvwxEvhQiMQ=="],
|
||||
|
||||
"@turf/distance": ["@turf/distance@7.3.4", "", { "dependencies": { "@turf/helpers": "7.3.4", "@turf/invariant": "7.3.4", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-9drWgd46uHPPyzgrcRQLgSvdS/SjVlQ6ZIBoRQagS5P2kSjUbcOXHIMeOSPwfxwlKhEtobLyr+IiR2ns1TfF8w=="],
|
||||
|
||||
"@turf/helpers": ["@turf/helpers@7.3.4", "", { "dependencies": { "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-U/S5qyqgx3WTvg4twaH0WxF3EixoTCfDsmk98g1E3/5e2YKp7JKYZdz0vivsS5/UZLJeZDEElOSFH4pUgp+l7g=="],
|
||||
@@ -783,10 +772,6 @@
|
||||
|
||||
"@turf/length": ["@turf/length@7.3.4", "", { "dependencies": { "@turf/distance": "7.3.4", "@turf/helpers": "7.3.4", "@turf/meta": "7.3.4", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-Dg1GnQ/B2go5NIWXt91N4L7XTjIgIWCftBSYIXkrpIM7QGjItzglek0Z5caytvb8ZRWXzZOGs8//+Q5we91WuQ=="],
|
||||
|
||||
"@turf/line-intersect": ["@turf/line-intersect@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0", "@turf/line-segment": "^6.5.0", "@turf/meta": "^6.5.0", "geojson-rbush": "3.x" } }, "sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA=="],
|
||||
|
||||
"@turf/line-segment": ["@turf/line-segment@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0", "@turf/meta": "^6.5.0" } }, "sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw=="],
|
||||
|
||||
"@turf/meta": ["@turf/meta@7.3.4", "", { "dependencies": { "@turf/helpers": "7.3.4", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-tlmw9/Hs1p2n0uoHVm1w3ugw1I6L8jv9YZrcdQa4SH5FX5UY0ATrKeIvfA55FlL//PGuYppJp+eyg/0eb4goqw=="],
|
||||
|
||||
"@turf/nearest-point-on-line": ["@turf/nearest-point-on-line@7.3.4", "", { "dependencies": { "@turf/distance": "7.3.4", "@turf/helpers": "7.3.4", "@turf/invariant": "7.3.4", "@turf/meta": "7.3.4", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-DQrP3lRju83rIXFN68tUEpc7ki/eRwdwBkK2CTT4RAcyCxbcH2NGJPQv8dYiww/Ar77u1WLVn+aINXZH904dWw=="],
|
||||
@@ -1485,8 +1470,6 @@
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"geojson-rbush": ["geojson-rbush@3.2.0", "", { "dependencies": { "@turf/bbox": "*", "@turf/helpers": "6.x", "@turf/meta": "6.x", "@types/geojson": "7946.0.8", "rbush": "^3.0.1" } }, "sha512-oVltQTXolxvsz1sZnutlSuLDEcQAKYC/uXt9zDzJJ6bu0W+baTI8LZBaTup5afzibEH4N3jlq2p+a152wlBJ7w=="],
|
||||
|
||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
@@ -2085,14 +2068,10 @@
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"quickselect": ["quickselect@2.0.0", "", {}, "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="],
|
||||
|
||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||
|
||||
"raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="],
|
||||
|
||||
"rbush": ["rbush@3.0.1", "", { "dependencies": { "quickselect": "^2.0.0" } }, "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w=="],
|
||||
|
||||
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
|
||||
|
||||
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
|
||||
@@ -2695,42 +2674,8 @@
|
||||
|
||||
"@react-native/dev-middleware/open": ["open@7.4.2", "", { "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" } }, "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="],
|
||||
|
||||
"@rnmapbox/maps/@turf/distance": ["@turf/distance@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" } }, "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg=="],
|
||||
|
||||
"@rnmapbox/maps/@turf/helpers": ["@turf/helpers@6.5.0", "", {}, "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="],
|
||||
|
||||
"@rnmapbox/maps/@turf/length": ["@turf/length@6.5.0", "", { "dependencies": { "@turf/distance": "^6.5.0", "@turf/helpers": "^6.5.0", "@turf/meta": "^6.5.0" } }, "sha512-5pL5/pnw52fck3oRsHDcSGrj9HibvtlrZ0QNy2OcW8qBFDNgZ4jtl6U7eATVoyWPKBHszW3dWETW+iLV7UARig=="],
|
||||
|
||||
"@rnmapbox/maps/@turf/nearest-point-on-line": ["@turf/nearest-point-on-line@6.5.0", "", { "dependencies": { "@turf/bearing": "^6.5.0", "@turf/destination": "^6.5.0", "@turf/distance": "^6.5.0", "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0", "@turf/line-intersect": "^6.5.0", "@turf/meta": "^6.5.0" } }, "sha512-WthrvddddvmymnC+Vf7BrkHGbDOUu6Z3/6bFYUGv1kxw8tiZ6n83/VG6kHz4poHOfS0RaNflzXSkmCi64fLBlg=="],
|
||||
|
||||
"@testing-library/react-native/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="],
|
||||
|
||||
"@turf/along/@turf/distance": ["@turf/distance@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0", "@turf/invariant": "^6.5.0" } }, "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg=="],
|
||||
|
||||
"@turf/along/@turf/helpers": ["@turf/helpers@6.5.0", "", {}, "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="],
|
||||
|
||||
"@turf/along/@turf/invariant": ["@turf/invariant@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg=="],
|
||||
|
||||
"@turf/bearing/@turf/helpers": ["@turf/helpers@6.5.0", "", {}, "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="],
|
||||
|
||||
"@turf/bearing/@turf/invariant": ["@turf/invariant@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg=="],
|
||||
|
||||
"@turf/destination/@turf/helpers": ["@turf/helpers@6.5.0", "", {}, "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="],
|
||||
|
||||
"@turf/destination/@turf/invariant": ["@turf/invariant@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg=="],
|
||||
|
||||
"@turf/line-intersect/@turf/helpers": ["@turf/helpers@6.5.0", "", {}, "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="],
|
||||
|
||||
"@turf/line-intersect/@turf/invariant": ["@turf/invariant@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg=="],
|
||||
|
||||
"@turf/line-intersect/@turf/meta": ["@turf/meta@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA=="],
|
||||
|
||||
"@turf/line-segment/@turf/helpers": ["@turf/helpers@6.5.0", "", {}, "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="],
|
||||
|
||||
"@turf/line-segment/@turf/invariant": ["@turf/invariant@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg=="],
|
||||
|
||||
"@turf/line-segment/@turf/meta": ["@turf/meta@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
@@ -2823,12 +2768,6 @@
|
||||
|
||||
"foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"geojson-rbush/@turf/helpers": ["@turf/helpers@6.5.0", "", {}, "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="],
|
||||
|
||||
"geojson-rbush/@turf/meta": ["@turf/meta@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA=="],
|
||||
|
||||
"geojson-rbush/@types/geojson": ["@types/geojson@7946.0.8", "", {}, "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA=="],
|
||||
|
||||
"glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
@@ -3097,14 +3036,6 @@
|
||||
|
||||
"@react-native/dev-middleware/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="],
|
||||
|
||||
"@rnmapbox/maps/@turf/distance/@turf/invariant": ["@turf/invariant@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg=="],
|
||||
|
||||
"@rnmapbox/maps/@turf/length/@turf/meta": ["@turf/meta@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA=="],
|
||||
|
||||
"@rnmapbox/maps/@turf/nearest-point-on-line/@turf/invariant": ["@turf/invariant@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg=="],
|
||||
|
||||
"@rnmapbox/maps/@turf/nearest-point-on-line/@turf/meta": ["@turf/meta@6.5.0", "", { "dependencies": { "@turf/helpers": "^6.5.0" } }, "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA=="],
|
||||
|
||||
"@testing-library/react-native/pretty-format/@jest/schemas": ["@jest/schemas@30.0.5", "", { "dependencies": { "@sinclair/typebox": "^0.34.0" } }, "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA=="],
|
||||
|
||||
"@testing-library/react-native/pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
||||
|
||||
542
components/Map/MapsV2Custom.tsx
Normal file
542
components/Map/MapsV2Custom.tsx
Normal file
@@ -0,0 +1,542 @@
|
||||
import { ReactNode, useCallback, useMemo, useState, useEffect } from "react";
|
||||
import {
|
||||
Image,
|
||||
StyleSheet,
|
||||
View,
|
||||
ViewStyle,
|
||||
StyleProp,
|
||||
Animated,
|
||||
Easing,
|
||||
} from "react-native";
|
||||
|
||||
import API_IMAGE from "@/constants/api-storage";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import {
|
||||
Camera,
|
||||
MapView,
|
||||
PointAnnotation,
|
||||
} from "@maplibre/maplibre-react-native";
|
||||
|
||||
// Style peta default
|
||||
const DEFAULT_MAP_STYLE = "https://tiles.openfreemap.org/styles/liberty";
|
||||
|
||||
// Region default (Bali, Indonesia)
|
||||
const DEFAULT_REGION = {
|
||||
latitude: -8.737109,
|
||||
longitude: 115.1756897,
|
||||
latitudeDelta: 0.1,
|
||||
longitudeDelta: 0.1,
|
||||
};
|
||||
|
||||
// Zoom level default
|
||||
const DEFAULT_ZOOM_LEVEL = 12;
|
||||
|
||||
// Ukuran marker default
|
||||
const DEFAULT_MARKER_SIZE = 30;
|
||||
|
||||
/**
|
||||
* Interface data marker untuk MapsV2Custom
|
||||
*/
|
||||
export interface MapMarker {
|
||||
id: string;
|
||||
coordinate: [number, number]; // [longitude, latitude]
|
||||
imageId?: string;
|
||||
imageUrl?: string;
|
||||
onSelected?: () => void;
|
||||
[key: string]: any; // Izinkan properti custom tambahan
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface region untuk positioning kamera
|
||||
*/
|
||||
export interface Region {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
latitudeDelta: number;
|
||||
longitudeDelta: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Props untuk komponen MapsV2Custom
|
||||
*/
|
||||
export interface MapsV2CustomProps {
|
||||
/** URL style peta custom (default: liberty style) */
|
||||
mapStyle?: string;
|
||||
|
||||
/** Override style container */
|
||||
style?: StyleProp<ViewStyle>;
|
||||
|
||||
/** Override style MapView */
|
||||
mapViewStyle?: StyleProp<ViewStyle>;
|
||||
|
||||
/** Region awal kamera */
|
||||
initialRegion?: Region;
|
||||
|
||||
/** Zoom level awal (default: 12) */
|
||||
zoomLevel?: number;
|
||||
|
||||
/**
|
||||
* Data marker - mendukung single marker atau array of markers
|
||||
* @example
|
||||
* // Single marker
|
||||
* markers={{ id: "1", coordinate: [115.175, -8.737], imageId: "abc" }}
|
||||
*
|
||||
* @example
|
||||
* // Multiple markers
|
||||
* markers={[
|
||||
* { id: "1", coordinate: [115.175, -8.737], imageId: "abc" },
|
||||
* { id: "2", coordinate: [115.180, -8.740], imageId: "xyz" }
|
||||
* ]}
|
||||
*/
|
||||
markers?: MapMarker | MapMarker[];
|
||||
|
||||
/** Custom renderer marker */
|
||||
renderMarker?: (marker: MapMarker) => ReactNode;
|
||||
|
||||
/** Callback ketika marker ditekan */
|
||||
onMarkerPress?: (marker: MapMarker) => void;
|
||||
|
||||
/** Gunakan style marker image default (default: true jika markers disediakan) */
|
||||
showDefaultMarkers?: boolean;
|
||||
|
||||
/** Ukuran untuk marker default (default: 30) */
|
||||
markerSize?: number;
|
||||
|
||||
/** Warna border untuk marker default */
|
||||
markerBorderColor?: string;
|
||||
|
||||
/** Children tambahan untuk MapView (custom overlays, dll.) */
|
||||
children?: ReactNode;
|
||||
|
||||
/** Handler untuk tekan pada peta */
|
||||
onMapPress?: (coordinates: [number, number]) => void;
|
||||
|
||||
/** Test identifier */
|
||||
testID?: string;
|
||||
|
||||
/** Props tambahan untuk Camera */
|
||||
cameraProps?: Partial<Omit<React.ComponentProps<typeof Camera>, "centerCoordinate" | "zoomLevel">>;
|
||||
|
||||
/** Props tambahan untuk MapView */
|
||||
mapViewProps?: Partial<React.ComponentProps<typeof MapView>>;
|
||||
|
||||
/** Props tambahan untuk PointAnnotation */
|
||||
annotationProps?: Partial<{
|
||||
id: string;
|
||||
title?: string;
|
||||
snippet?: string;
|
||||
selected?: boolean;
|
||||
draggable?: boolean;
|
||||
coordinate: number[];
|
||||
anchor?: { x: number; y: number };
|
||||
onSelected?: (payload: any) => void;
|
||||
onDeselected?: (payload: any) => void;
|
||||
onDragStart?: (payload: any) => void;
|
||||
onDragEnd?: (payload: any) => void;
|
||||
onDrag?: (payload: any) => void;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalisasi markers ke array - mendukung single marker atau array
|
||||
*/
|
||||
function normalizeMarkers(markers: MapMarker | MapMarker[] | undefined): MapMarker[] {
|
||||
if (!markers) return [];
|
||||
return Array.isArray(markers) ? markers : [markers];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validasi marker memiliki props yang required (hanya development mode)
|
||||
*/
|
||||
function validateMarker(marker: MapMarker, index: number): boolean {
|
||||
if (!marker.id) {
|
||||
console.warn(`[MapsV2Custom] Marker pada index ${index} tidak memiliki prop 'id' yang required`);
|
||||
return false;
|
||||
}
|
||||
if (!marker.coordinate || !Array.isArray(marker.coordinate) || marker.coordinate.length !== 2) {
|
||||
console.warn(`[MapsV2Custom] Marker pada index ${index} tidak memiliki prop 'coordinate' yang required. Format yang diharapkan: [longitude, latitude]`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Komponen skeleton untuk loading state dengan shimmer animation
|
||||
*/
|
||||
function SkeletonMarker({
|
||||
size = DEFAULT_MARKER_SIZE,
|
||||
borderColor = MainColor.darkblue,
|
||||
loadingColor = "#C5C5C5",
|
||||
}: {
|
||||
size?: number;
|
||||
borderColor?: string;
|
||||
loadingColor?: string;
|
||||
}) {
|
||||
const shimmerAnim = useMemo(() => new Animated.Value(0), []);
|
||||
|
||||
useEffect(() => {
|
||||
const animation = Animated.loop(
|
||||
Animated.sequence([
|
||||
Animated.timing(shimmerAnim, {
|
||||
toValue: 1,
|
||||
duration: 800,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(shimmerAnim, {
|
||||
toValue: 0,
|
||||
duration: 800,
|
||||
easing: Easing.inOut(Easing.ease),
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
animation.start();
|
||||
|
||||
return () => animation.stop();
|
||||
}, [shimmerAnim]);
|
||||
|
||||
const shimmerOpacity = shimmerAnim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0.3, 0.7],
|
||||
});
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.markerContainer,
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: size / 2,
|
||||
borderColor,
|
||||
backgroundColor: loadingColor,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.skeletonShimmer,
|
||||
{
|
||||
opacity: shimmerOpacity,
|
||||
backgroundColor: "#FFFFFF",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Komponen fallback untuk error state
|
||||
*/
|
||||
function FallbackMarker({
|
||||
size = DEFAULT_MARKER_SIZE,
|
||||
borderColor = MainColor.darkblue,
|
||||
iconColor = MainColor.darkblue,
|
||||
}: {
|
||||
size?: number;
|
||||
borderColor?: string;
|
||||
iconColor?: string;
|
||||
}) {
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.markerContainer,
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: size / 2,
|
||||
borderColor,
|
||||
backgroundColor: "#F5F5F5",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={[styles.fallbackIcon, { borderColor: iconColor }]}>
|
||||
<View style={[styles.fallbackIconInner, { backgroundColor: iconColor }]} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Props untuk DefaultMarker component
|
||||
*/
|
||||
export interface DefaultMarkerProps {
|
||||
/** ID file image dari API */
|
||||
imageId?: string;
|
||||
/** URL image langsung */
|
||||
imageUrl?: string;
|
||||
/** Ukuran marker (default: 30) */
|
||||
size?: number;
|
||||
/** Warna border marker (default: darkblue) */
|
||||
borderColor?: string;
|
||||
/** Warna skeleton loading (default: gray) */
|
||||
loadingColor?: string;
|
||||
/** Warna icon fallback (default: darkblue) */
|
||||
fallbackIconColor?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Komponen marker default dengan image, border, shadows, loading skeleton, dan error fallback
|
||||
*/
|
||||
export function DefaultMarker({
|
||||
imageId,
|
||||
imageUrl,
|
||||
size = DEFAULT_MARKER_SIZE,
|
||||
borderColor = MainColor.darkblue,
|
||||
loadingColor = MainColor.white_gray,
|
||||
fallbackIconColor = MainColor.darkblue,
|
||||
}: DefaultMarkerProps) {
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const uri = imageId ? API_IMAGE.GET({ fileId: imageId }) : imageUrl;
|
||||
|
||||
// Debug log untuk development
|
||||
if (__DEV__ && uri) {
|
||||
console.log("[DefaultMarker] Image URI:", uri);
|
||||
}
|
||||
|
||||
const handleError = useCallback((error: any) => {
|
||||
console.log("[DefaultMarker] Image error:", error?.nativeEvent?.error || error);
|
||||
setHasError(true);
|
||||
}, []);
|
||||
|
||||
const handleLoad = useCallback(() => {
|
||||
console.log("[DefaultMarker] Image loaded successfully");
|
||||
}, []);
|
||||
|
||||
// Jika tidak ada URI atau error, tampilkan fallback
|
||||
if (!uri || hasError) {
|
||||
return (
|
||||
<FallbackMarker
|
||||
size={size}
|
||||
borderColor={borderColor}
|
||||
iconColor={fallbackIconColor}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Render image dengan placeholder (defaultSource) untuk loading state
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.markerContainer,
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: size / 2,
|
||||
borderColor,
|
||||
backgroundColor: loadingColor, // Background color sebagai placeholder
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
source={{ uri }}
|
||||
style={[styles.markerImage, { width: size, height: size }]}
|
||||
resizeMode="cover"
|
||||
onLoad={handleLoad}
|
||||
onError={handleError}
|
||||
// Placeholder untuk Android saat loading
|
||||
defaultSource={require("@/assets/images/icon.png")}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Komponen Map yang reusable dan customizable menggunakan Mapbox/MapLibre
|
||||
*
|
||||
* Mendukung single marker, multiple markers, atau empty state.
|
||||
*
|
||||
* @example
|
||||
* // Single marker
|
||||
* <MapsV2Custom
|
||||
* markers={{
|
||||
* id: "1",
|
||||
* coordinate: [115.1756897, -8.737109],
|
||||
* imageId: "file-123"
|
||||
* }}
|
||||
* />
|
||||
*
|
||||
* @example
|
||||
* // Multiple markers
|
||||
* <MapsV2Custom
|
||||
* markers={[
|
||||
* { id: "1", coordinate: [115.175, -8.737], imageId: "abc" },
|
||||
* { id: "2", coordinate: [115.180, -8.740], imageId: "xyz" }
|
||||
* ]}
|
||||
* />
|
||||
*
|
||||
* @example
|
||||
* // Peta dengan custom style dan custom markers
|
||||
* <MapsV2Custom
|
||||
* mapStyle="https://your-custom-style.com"
|
||||
* markers={markers}
|
||||
* markerSize={40}
|
||||
* markerBorderColor={MainColor.primary}
|
||||
* />
|
||||
*
|
||||
* @example
|
||||
* // Dengan custom marker renderer
|
||||
* <MapsV2Custom
|
||||
* markers={data}
|
||||
* renderMarker={(marker) => <CustomMarker {...marker} />}
|
||||
* />
|
||||
*
|
||||
* @example
|
||||
* // Peta kosong (tanpa markers)
|
||||
* <MapsV2Custom
|
||||
* initialRegion={{ latitude: -6.2, longitude: 106.8, latitudeDelta: 0.1, longitudeDelta: 0.1 }}
|
||||
* />
|
||||
*/
|
||||
export function MapsV2Custom({
|
||||
mapStyle = DEFAULT_MAP_STYLE,
|
||||
style = styles.container,
|
||||
mapViewStyle = styles.map,
|
||||
initialRegion = DEFAULT_REGION,
|
||||
zoomLevel = DEFAULT_ZOOM_LEVEL,
|
||||
markers,
|
||||
renderMarker,
|
||||
onMarkerPress,
|
||||
showDefaultMarkers = true,
|
||||
markerSize = DEFAULT_MARKER_SIZE,
|
||||
markerBorderColor = MainColor.darkblue,
|
||||
children,
|
||||
onMapPress,
|
||||
testID,
|
||||
cameraProps,
|
||||
mapViewProps,
|
||||
annotationProps,
|
||||
}: MapsV2CustomProps) {
|
||||
// Normalisasi markers ke array (mendukung single atau multiple)
|
||||
const normalizedMarkers = useMemo(
|
||||
() => {
|
||||
const arr = normalizeMarkers(markers);
|
||||
// Filter marker yang invalid
|
||||
return arr.filter((marker) => {
|
||||
if (!marker.id) {
|
||||
console.warn("[MapsV2Custom] Marker tanpa id akan diabaikan");
|
||||
return false;
|
||||
}
|
||||
if (!marker.coordinate || marker.coordinate.length !== 2) {
|
||||
console.warn("[MapsV2Custom] Marker tanpa coordinate valid akan diabaikan");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
[markers]
|
||||
);
|
||||
|
||||
// Validasi markers dalam development mode
|
||||
useMemo(() => {
|
||||
if (__DEV__) {
|
||||
normalizedMarkers.forEach((marker, index) => {
|
||||
validateMarker(marker, index);
|
||||
});
|
||||
}
|
||||
}, [normalizedMarkers]);
|
||||
|
||||
const handleMarkerSelected = useCallback(
|
||||
(marker: MapMarker) => {
|
||||
if (marker.onSelected) {
|
||||
marker.onSelected();
|
||||
}
|
||||
if (onMarkerPress) {
|
||||
onMarkerPress(marker);
|
||||
}
|
||||
},
|
||||
[onMarkerPress]
|
||||
);
|
||||
|
||||
const renderMarkerComponent = useCallback(
|
||||
(marker: MapMarker): ReactNode => {
|
||||
if (renderMarker) {
|
||||
return renderMarker(marker);
|
||||
}
|
||||
|
||||
if (showDefaultMarkers) {
|
||||
return (
|
||||
<DefaultMarker
|
||||
imageId={marker.imageId}
|
||||
imageUrl={marker.imageUrl}
|
||||
size={markerSize}
|
||||
borderColor={markerBorderColor}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
[renderMarker, showDefaultMarkers, markerSize, markerBorderColor]
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={style} testID={testID}>
|
||||
<MapView style={mapViewStyle} mapStyle={mapStyle} {...mapViewProps}>
|
||||
<Camera
|
||||
zoomLevel={zoomLevel}
|
||||
centerCoordinate={[initialRegion.longitude, initialRegion.latitude]}
|
||||
{...cameraProps}
|
||||
/>
|
||||
|
||||
{normalizedMarkers.map((marker) => (
|
||||
<PointAnnotation
|
||||
key={marker.id}
|
||||
id={marker.id}
|
||||
coordinate={marker.coordinate}
|
||||
onSelected={() => handleMarkerSelected(marker)}
|
||||
{...annotationProps}
|
||||
>
|
||||
{renderMarkerComponent(marker) as any}
|
||||
</PointAnnotation>
|
||||
))}
|
||||
|
||||
{children}
|
||||
</MapView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1 },
|
||||
map: { flex: 1 },
|
||||
markerContainer: {
|
||||
overflow: "hidden",
|
||||
borderWidth: 1,
|
||||
elevation: 4,
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 3,
|
||||
},
|
||||
markerImage: {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
},
|
||||
skeletonShimmer: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
borderRadius: 999,
|
||||
},
|
||||
fallbackIcon: {
|
||||
width: "60%",
|
||||
height: "60%",
|
||||
borderRadius: 999,
|
||||
borderWidth: 2,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
fallbackIconInner: {
|
||||
width: "40%",
|
||||
height: "40%",
|
||||
borderRadius: 999,
|
||||
},
|
||||
});
|
||||
272
components/Map/SelectLocationMap.tsx
Normal file
272
components/Map/SelectLocationMap.tsx
Normal file
@@ -0,0 +1,272 @@
|
||||
import React, { useState, useCallback, useRef } from "react";
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
ActivityIndicator,
|
||||
} from "react-native";
|
||||
import {
|
||||
MapView,
|
||||
Camera,
|
||||
PointAnnotation,
|
||||
MarkerView,
|
||||
} from "@maplibre/maplibre-react-native";
|
||||
import * as Location from "expo-location";
|
||||
import { useFocusEffect, useRouter } from "expo-router";
|
||||
|
||||
const MAP_STYLE = "https://tiles.openfreemap.org/styles/liberty";
|
||||
|
||||
type Coordinate = {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
};
|
||||
|
||||
export default function SelectLocationMap() {
|
||||
const router = useRouter();
|
||||
const annotationRef = useRef<any>(null);
|
||||
|
||||
const [selectedCoord, setSelectedCoord] = useState<Coordinate | null>(null);
|
||||
const [address, setAddress] = useState<string>("");
|
||||
const [isLoadingAddress, setIsLoadingAddress] = useState(false);
|
||||
const [cameraCenter, setCameraCenter] = useState<[number, number]>([
|
||||
106.8272, -6.1751,
|
||||
]);
|
||||
|
||||
const reverseGeocode = async (coord: Coordinate): Promise<string> => {
|
||||
try {
|
||||
const { status } = await Location.getForegroundPermissionsAsync();
|
||||
if (status !== "granted") {
|
||||
await Location.requestForegroundPermissionsAsync();
|
||||
}
|
||||
|
||||
const results = await Location.reverseGeocodeAsync({
|
||||
latitude: coord.latitude,
|
||||
longitude: coord.longitude,
|
||||
});
|
||||
|
||||
if (!results || results.length === 0) return "Alamat tidak ditemukan";
|
||||
|
||||
const loc = results[0];
|
||||
const parts = [
|
||||
loc.street,
|
||||
loc.district,
|
||||
loc.subregion,
|
||||
loc.city,
|
||||
loc.region,
|
||||
loc.country,
|
||||
].filter(Boolean);
|
||||
|
||||
return parts.length > 0 ? parts.join(", ") : "Alamat tidak ditemukan";
|
||||
} catch (error: any) {
|
||||
console.log("reverseGeocode error:", error?.message || error);
|
||||
return "Gagal mengambil alamat";
|
||||
}
|
||||
};
|
||||
|
||||
const handleMapPress = useCallback(async (event: any) => {
|
||||
try {
|
||||
const coordinates = event?.geometry?.coordinates;
|
||||
if (!coordinates) return;
|
||||
|
||||
const [longitude, latitude] = coordinates;
|
||||
if (!longitude || !latitude) return;
|
||||
|
||||
const coord: Coordinate = { latitude, longitude };
|
||||
|
||||
// ✅ Update state koordinat, BUKAN ganti key
|
||||
setSelectedCoord(coord);
|
||||
setCameraCenter([longitude, latitude]);
|
||||
setAddress("");
|
||||
setIsLoadingAddress(true);
|
||||
|
||||
const resolvedAddress = await reverseGeocode(coord);
|
||||
setAddress(resolvedAddress);
|
||||
setIsLoadingAddress(false);
|
||||
|
||||
// ✅ Refresh annotation tanpa unmount
|
||||
annotationRef.current?.refresh?.();
|
||||
} catch (error: any) {
|
||||
console.log("handleMapPress error:", error?.message || error);
|
||||
setIsLoadingAddress(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!selectedCoord) return;
|
||||
router.navigate({
|
||||
pathname: "/maps/create",
|
||||
params: {
|
||||
latitude: String(selectedCoord.latitude),
|
||||
longitude: String(selectedCoord.longitude),
|
||||
address,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Sembunyikan marker sebelum halaman unmount
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
return () => {
|
||||
// Cleanup saat leave — sembunyikan marker dulu sebelum unmount
|
||||
setSelectedCoord(null);
|
||||
};
|
||||
}, []),
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<MapView style={styles.map} mapStyle={MAP_STYLE} onPress={handleMapPress}>
|
||||
<Camera
|
||||
zoomLevel={14}
|
||||
centerCoordinate={cameraCenter}
|
||||
animationMode="flyTo"
|
||||
animationDuration={300}
|
||||
/>
|
||||
|
||||
{/* ✅ Key statis — tidak pernah berubah, tidak unmount/remount */}
|
||||
{selectedCoord && (
|
||||
<MarkerView
|
||||
id="selected-marker"
|
||||
coordinate={[selectedCoord.longitude, selectedCoord.latitude]}
|
||||
anchor={{ x: 0.5, y: 1 }} // Anchor bawah tengah
|
||||
>
|
||||
<View style={styles.pin}>
|
||||
<View style={styles.pinDot} />
|
||||
</View>
|
||||
</MarkerView>
|
||||
)}
|
||||
</MapView>
|
||||
|
||||
{/* Bottom Sheet */}
|
||||
<View style={styles.bottomSheet}>
|
||||
{!selectedCoord ? (
|
||||
<Text style={styles.hintText}>
|
||||
Tap pada peta untuk memilih lokasi
|
||||
</Text>
|
||||
) : (
|
||||
<>
|
||||
<View style={styles.coordRow}>
|
||||
<View style={styles.coordItem}>
|
||||
<Text style={styles.coordLabel}>Latitude</Text>
|
||||
<Text style={styles.coordValue}>
|
||||
{selectedCoord.latitude.toFixed(6)}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.dividerVertical} />
|
||||
<View style={styles.coordItem}>
|
||||
<Text style={styles.coordLabel}>Longitude</Text>
|
||||
<Text style={styles.coordValue}>
|
||||
{selectedCoord.longitude.toFixed(6)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.addressContainer}>
|
||||
<Text style={styles.coordLabel}>Alamat</Text>
|
||||
{isLoadingAddress ? (
|
||||
<ActivityIndicator size="small" color="#0a1f44" />
|
||||
) : (
|
||||
<Text style={styles.addressText} numberOfLines={2}>
|
||||
{address || "-"}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.confirmButton,
|
||||
isLoadingAddress && styles.confirmButtonDisabled,
|
||||
]}
|
||||
onPress={handleConfirm}
|
||||
disabled={isLoadingAddress}
|
||||
>
|
||||
<Text style={styles.confirmButtonText}>Konfirmasi Lokasi</Text>
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1 },
|
||||
map: { flex: 1 },
|
||||
pin: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 100,
|
||||
backgroundColor: "#0a1f44",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderWidth: 2,
|
||||
borderColor: "#fff",
|
||||
},
|
||||
pinDot: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 100,
|
||||
backgroundColor: "#fff",
|
||||
},
|
||||
bottomSheet: {
|
||||
backgroundColor: "#fff",
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 20,
|
||||
paddingBottom: 32,
|
||||
borderTopLeftRadius: 20,
|
||||
borderTopRightRadius: 20,
|
||||
elevation: 10,
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: -3 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 6,
|
||||
minHeight: 140,
|
||||
justifyContent: "center",
|
||||
},
|
||||
hintText: {
|
||||
textAlign: "center",
|
||||
color: "#888",
|
||||
fontSize: 14,
|
||||
},
|
||||
coordRow: {
|
||||
flexDirection: "row",
|
||||
marginBottom: 12,
|
||||
},
|
||||
coordItem: { flex: 1 },
|
||||
dividerVertical: {
|
||||
width: 1,
|
||||
backgroundColor: "#e0e0e0",
|
||||
marginHorizontal: 12,
|
||||
},
|
||||
coordLabel: {
|
||||
fontSize: 11,
|
||||
color: "#888",
|
||||
marginBottom: 2,
|
||||
},
|
||||
coordValue: {
|
||||
fontSize: 14,
|
||||
fontWeight: "600",
|
||||
color: "#0a1f44",
|
||||
},
|
||||
addressContainer: { marginBottom: 16 },
|
||||
addressText: {
|
||||
fontSize: 13,
|
||||
color: "#333",
|
||||
lineHeight: 18,
|
||||
},
|
||||
confirmButton: {
|
||||
backgroundColor: "#0a1f44",
|
||||
borderRadius: 12,
|
||||
paddingVertical: 14,
|
||||
alignItems: "center",
|
||||
},
|
||||
confirmButtonDisabled: {
|
||||
backgroundColor: "#aaa",
|
||||
},
|
||||
confirmButtonText: {
|
||||
color: "#fff",
|
||||
fontWeight: "700",
|
||||
fontSize: 15,
|
||||
},
|
||||
});
|
||||
101
docs/PODS.back
Normal file
101
docs/PODS.back
Normal file
@@ -0,0 +1,101 @@
|
||||
NOTE:
|
||||
|
||||
Untuk Development Selanjutnya:
|
||||
Sekarang Anda bisa menjalankan:
|
||||
|
||||
1 # Untuk run iOS dev client
|
||||
2 bun run ios
|
||||
3
|
||||
4 # Atau dengan Expo
|
||||
5 bunx expo run:ios
|
||||
|
||||
Jika di masa depan terjadi error serupa, Anda bisa gunakan command ini:
|
||||
|
||||
1 cd ios
|
||||
2 rm -rf Pods Podfile.lock
|
||||
3 pod install
|
||||
|
||||
|
||||
|
||||
use_modular_headers!
|
||||
|
||||
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
|
||||
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
|
||||
|
||||
require 'json'
|
||||
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
|
||||
|
||||
ENV['RCT_NEW_ARCH_ENABLED'] ||= '0' if podfile_properties['newArchEnabled'] == 'false'
|
||||
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
|
||||
ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
|
||||
ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
|
||||
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
|
||||
|
||||
prepare_react_native_project!
|
||||
|
||||
target 'HIPMIBadungConnect' do
|
||||
use_expo_modules!
|
||||
|
||||
if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
|
||||
config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
|
||||
else
|
||||
config_command = [
|
||||
'npx',
|
||||
'expo-modules-autolinking',
|
||||
'react-native-config',
|
||||
'--json',
|
||||
'--platform',
|
||||
'ios'
|
||||
]
|
||||
end
|
||||
|
||||
config = use_native_modules!(config_command)
|
||||
|
||||
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
|
||||
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
|
||||
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
:hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
|
||||
# An absolute path to your application root.
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/..",
|
||||
:privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
|
||||
)
|
||||
|
||||
pod 'Firebase'
|
||||
pod 'Firebase/Messaging'
|
||||
|
||||
# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403
|
||||
post_install do |installer|
|
||||
# @generated begin @maplibre/maplibre-react-native:post-install - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472
|
||||
$MLRN.post_install(installer)
|
||||
# @generated end @maplibre/maplibre-react-native:post-install
|
||||
|
||||
# Fix all script phases with incorrect paths
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_phases.each do |phase|
|
||||
next unless phase.respond_to?(:shell_script)
|
||||
|
||||
# Fix duplicated path issue
|
||||
if phase.shell_script.include?('with-environment.sh')
|
||||
# Remove any existing path and use proper relative path
|
||||
phase.shell_script = phase.shell_script.gsub(
|
||||
%r{(/.*?/node_modules/react-native)+/scripts/xcode/with-environment.sh},
|
||||
'${PODS_ROOT}/../../node_modules/react-native/scripts/xcode/with-environment.sh'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Standard React Native post install
|
||||
react_native_post_install(
|
||||
installer,
|
||||
config[:reactNativePath],
|
||||
:mac_catalyst_enabled => false,
|
||||
:ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true',
|
||||
)
|
||||
end
|
||||
# @generated end post_installer
|
||||
|
||||
end
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"originHash" : "e70d3525c8e2819a8b34f22909815dab5c700c25a06c32388f3930f7b3627768",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "maplibre-gl-native-distribution",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/maplibre/maplibre-gl-native-distribution",
|
||||
"state" : {
|
||||
"revision" : "c68c970ff3ece56cfc3b36849db70167fa208beb",
|
||||
"version" : "6.17.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
10
ios/Podfile
10
ios/Podfile
@@ -35,13 +35,6 @@ target 'HIPMIBadungConnect' do
|
||||
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
|
||||
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
|
||||
|
||||
# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-c8812095000d6054b846ce74840f0ffb540c2757
|
||||
pre_install do |installer|
|
||||
# @generated begin @rnmapbox/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-ea4905840bf9fcea0acc62e92aa2e784f9d760f8
|
||||
$RNMapboxMaps.pre_install(installer)
|
||||
# @generated end @rnmapbox/maps-pre_installer
|
||||
end
|
||||
# @generated end pre_installer
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
@@ -59,9 +52,6 @@ target 'HIPMIBadungConnect' do
|
||||
# @generated begin @maplibre/maplibre-react-native:post-install - expo prebuild (DO NOT MODIFY) sync-6e76c80af0d70c0003d06822dd59b7c729fca472
|
||||
$MLRN.post_install(installer)
|
||||
# @generated end @maplibre/maplibre-react-native:post-install
|
||||
# @generated begin @rnmapbox/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-c4e8f90e96f6b6c6ea9241dd7b52ab5f57f7bf36
|
||||
$RNMapboxMaps.post_install(installer)
|
||||
# @generated end @rnmapbox/maps-post_installer
|
||||
|
||||
# Fix all script phases with incorrect paths
|
||||
installer.pods_project.targets.each do |target|
|
||||
|
||||
969
ios/Podfile.lock
969
ios/Podfile.lock
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,6 @@
|
||||
"@react-navigation/elements": "^2.3.8",
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/native-stack": "^7.3.10",
|
||||
"@rnmapbox/maps": "^10.2.7",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/react-native-vector-icons": "^6.4.18",
|
||||
"axios": "^1.11.0",
|
||||
|
||||
125
screens/Maps/DrawerMaps.tsx
Normal file
125
screens/Maps/DrawerMaps.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
import {
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
Grid,
|
||||
ButtonCustom,
|
||||
} from "@/components";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
||||
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
|
||||
interface TypeDrawerMaps {
|
||||
openDrawer: boolean;
|
||||
setOpenDrawer: (value: boolean) => void;
|
||||
selected: {
|
||||
id: string;
|
||||
bidangBisnis: string;
|
||||
nomorTelepon: string;
|
||||
alamatBisnis: string;
|
||||
namePin: string;
|
||||
imageId: string;
|
||||
portofolioId: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
};
|
||||
}
|
||||
|
||||
export default function DrawerMaps({
|
||||
openDrawer,
|
||||
setOpenDrawer,
|
||||
selected,
|
||||
}: TypeDrawerMaps) {
|
||||
return (
|
||||
<DrawerCustom
|
||||
isVisible={openDrawer}
|
||||
closeDrawer={() => setOpenDrawer(false)}
|
||||
height={"auto"}
|
||||
>
|
||||
<DummyLandscapeImage height={200} imageId={selected.imageId} />
|
||||
<Spacing />
|
||||
<StackCustom gap={"xs"}>
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftItem={
|
||||
<FontAwesome
|
||||
name="building-o"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightItem={<TextCustom>{selected.namePin}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="list-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightItem={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="call-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightItem={<TextCustom>{selected.nomorTelepon}</TextCustom>}
|
||||
/>
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="location-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightItem={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
||||
/>
|
||||
|
||||
<Grid>
|
||||
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
setOpenDrawer(false);
|
||||
router.push(`/portofolio/${selected.portofolioId}`);
|
||||
}}
|
||||
>
|
||||
Detail
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ paddingLeft: 10 }}>
|
||||
<ButtonCustom
|
||||
onPress={() => {
|
||||
openInDeviceMaps({
|
||||
latitude: selected.latitude,
|
||||
longitude: selected.longitude,
|
||||
title: selected.namePin,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Buka Maps
|
||||
</ButtonCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</StackCustom>
|
||||
</DrawerCustom>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +1,115 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
// Cek versi >= 10.x gunakan ini
|
||||
import { MapView, Camera, PointAnnotation } from "@maplibre/maplibre-react-native";
|
||||
import { apiMapsGetAll } from "@/service/api-client/api-maps";
|
||||
import { useFocusEffect } from "expo-router";
|
||||
|
||||
const MAP_STYLE = "https://tiles.openfreemap.org/styles/liberty";
|
||||
import { ViewWrapper } from "@/components";
|
||||
import { MapMarker, MapsV2Custom } from "@/components/Map/MapsV2Custom";
|
||||
|
||||
import DrawerMaps from "./DrawerMaps";
|
||||
import { StyleSheet } from "react-native";
|
||||
|
||||
interface TypeMaps {
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
namePin: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
authorId: string;
|
||||
portofolioId: string;
|
||||
imageId: string;
|
||||
pinId: string | null;
|
||||
Portofolio: {
|
||||
id: string;
|
||||
namaBisnis: string;
|
||||
logoId: string;
|
||||
alamatKantor: string;
|
||||
tlpn: string;
|
||||
MasterBidangBisnis: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default function MapsView2() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<MapView
|
||||
style={styles.map}
|
||||
mapStyle={MAP_STYLE}
|
||||
>
|
||||
<Camera
|
||||
zoomLevel={12}
|
||||
centerCoordinate={[115.1756897, -8.737109]}
|
||||
/>
|
||||
const [list, setList] = useState<TypeMaps[] | null>(null);
|
||||
const [loadList, setLoadList] = useState(false);
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const [selected, setSelected] = useState({
|
||||
id: "",
|
||||
bidangBisnis: "",
|
||||
nomorTelepon: "",
|
||||
alamatBisnis: "",
|
||||
namePin: "",
|
||||
imageId: "",
|
||||
portofolioId: "",
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
});
|
||||
|
||||
<PointAnnotation
|
||||
id="marker-1"
|
||||
coordinate={[115.1756897, -8.737109]}
|
||||
>
|
||||
<View style={styles.marker} />
|
||||
</PointAnnotation>
|
||||
</MapView>
|
||||
</View>
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
handlerLoadList();
|
||||
}, []),
|
||||
);
|
||||
|
||||
const handlerLoadList = async () => {
|
||||
try {
|
||||
setLoadList(true);
|
||||
const response = await apiMapsGetAll();
|
||||
|
||||
if (response.success) {
|
||||
setList(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const markers: MapMarker[] = list?.map((item) => ({
|
||||
id: item.id,
|
||||
coordinate: [item.longitude, item.latitude] as [number, number],
|
||||
imageId: item.Portofolio.logoId,
|
||||
onSelected: () => {
|
||||
setOpenDrawer(true);
|
||||
setSelected({
|
||||
id: item.id,
|
||||
bidangBisnis: item.Portofolio.MasterBidangBisnis.name,
|
||||
nomorTelepon: item.Portofolio.tlpn,
|
||||
alamatBisnis: item.Portofolio.alamatKantor,
|
||||
namePin: item.namePin,
|
||||
imageId: item.imageId,
|
||||
portofolioId: item.Portofolio.id,
|
||||
latitude: item.latitude,
|
||||
longitude: item.longitude,
|
||||
});
|
||||
},
|
||||
})) || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper>
|
||||
<MapsV2Custom markers={markers} />
|
||||
</ViewWrapper>
|
||||
|
||||
<DrawerMaps
|
||||
openDrawer={openDrawer}
|
||||
setOpenDrawer={setOpenDrawer}
|
||||
selected={selected}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1 },
|
||||
map: { flex: 1 },
|
||||
marker: {
|
||||
width: 16,
|
||||
height: 16,
|
||||
borderRadius: 8,
|
||||
backgroundColor: "red",
|
||||
map: {
|
||||
flex: 1,
|
||||
width: "50%",
|
||||
maxHeight: "50%",
|
||||
},
|
||||
});
|
||||
@@ -1,19 +1,42 @@
|
||||
import {
|
||||
BaseBox,
|
||||
MapCustom,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
|
||||
import { BaseBox, StackCustom, TextCustom } from "@/components";
|
||||
import { MapsV2Custom } from "@/components/Map/MapsV2Custom";
|
||||
|
||||
export default function Portofolio_BusinessLocation({
|
||||
data,
|
||||
imageId,
|
||||
setOpenDrawerLocation,
|
||||
}: {
|
||||
data: any;
|
||||
data: {
|
||||
id: string;
|
||||
imageId: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
namePin: string;
|
||||
pinId: string;
|
||||
} | null;
|
||||
|
||||
imageId?: string;
|
||||
setOpenDrawerLocation: (value: boolean) => void;
|
||||
}) {
|
||||
console.log("data", data);
|
||||
|
||||
// Buat marker hanya jika data lengkap
|
||||
const markers =
|
||||
data?.latitude && data?.longitude
|
||||
? [
|
||||
{
|
||||
id: data.id || "location-marker",
|
||||
coordinate: [data.longitude, data.latitude] as [number, number],
|
||||
imageId,
|
||||
onSelected: () => {
|
||||
setOpenDrawerLocation(true);
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseBox style={{ height: !data ? 200 : "auto" }}>
|
||||
@@ -30,18 +53,33 @@ export default function Portofolio_BusinessLocation({
|
||||
Lokasi bisnis belum ditambahkan
|
||||
</TextCustom>
|
||||
) : (
|
||||
<MapCustom
|
||||
latitude={data?.latitude}
|
||||
longitude={data?.longitude}
|
||||
namePin={data?.namePin}
|
||||
imageId={imageId}
|
||||
onPress={() => {
|
||||
setOpenDrawerLocation(true);
|
||||
}}
|
||||
/>
|
||||
<View style={styles.mapContainer}>
|
||||
<MapsV2Custom
|
||||
markers={markers}
|
||||
zoomLevel={15}
|
||||
showDefaultMarkers={true}
|
||||
markerSize={35}
|
||||
initialRegion={{
|
||||
latitude: data?.latitude,
|
||||
longitude: data?.longitude,
|
||||
latitudeDelta: 0.1,
|
||||
longitudeDelta: 0.1,
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
mapContainer: {
|
||||
width: "100%",
|
||||
height: 250,
|
||||
borderRadius: 8,
|
||||
overflow: "hidden",
|
||||
marginTop: 8,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user