diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ee44e41a..f1f522af 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,10 +12,10 @@ jobs: uses: actions/checkout@v4 - name: 📥 Checkout PR - uses: goat-community/.github/.github/actions/pr-git-checkout + uses: goat-community/.github/.github/actions/pr-git-checkout@main - name: 📥 Monorepo install - uses: goat-community/.github/.github/actions/pnpm-install + uses: goat-community/.github/.github/actions/pnpm-install@main with: enable-corepack: true cwd: ${{ github.workspace }} diff --git a/.github/workflows/nextjs-bundle-analysis.yml b/.github/workflows/nextjs-bundle-analysis.yml index 0aab7e65..df89148b 100644 --- a/.github/workflows/nextjs-bundle-analysis.yml +++ b/.github/workflows/nextjs-bundle-analysis.yml @@ -21,10 +21,10 @@ jobs: - name: 📥 Checkout PR if: ${{ github.event_name == 'pull_request' }} - uses: goat-community/.github/.github/actions/pr-git-checkout + uses: goat-community/.github/.github/actions/pr-git-checkout@main - name: 📥 Monorepo install - uses: goat-community/.github/.github/actions/pnpm-install + uses: goat-community/.github/.github/actions/pnpm-install@main with: enable-corepack: true cwd: ${{ github.workspace }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a568bcc5..ce740713 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -13,6 +13,8 @@ name: "Client - PR" on: + pull_request: + branches: [main] pull_request_target: branches: [main] merge_group: @@ -37,7 +39,7 @@ jobs: typecheck: name: typecheck - uses: ./.github/workflows/check-types.yml + uses: ./.github/workflows/typecheck.yml secrets: inherit lint: diff --git a/.github/workflows/production-build.yml b/.github/workflows/production-build.yml index d755eae3..f83190c4 100644 --- a/.github/workflows/production-build.yml +++ b/.github/workflows/production-build.yml @@ -14,10 +14,10 @@ jobs: uses: actions/checkout@v4 - name: 📥 Checkout PR - uses: goat-community/.github/.github/actions/pr-git-checkout + uses: goat-community/.github/.github/actions/pr-git-checkout@main - name: 📥 Monorepo install - uses: goat-community/.github/.github/actions/pnpm-install + uses: goat-community/.github/.github/actions/pnpm-install@main with: enable-corepack: true cwd: ${{ github.workspace }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ac3a5c6..0790532f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ concurrency: jobs: release-docker: name: release - uses: "goat-community/.github/.github/workflows/reusable-docker-build.yml@main" + uses: goat-community/.github/.github/workflows/reusable-docker-build.yml@main permissions: contents: read packages: write diff --git a/.github/workflows/check-types.yml b/.github/workflows/typecheck.yml similarity index 97% rename from .github/workflows/check-types.yml rename to .github/workflows/typecheck.yml index 55d371a4..1f497199 100644 --- a/.github/workflows/check-types.yml +++ b/.github/workflows/typecheck.yml @@ -13,10 +13,10 @@ jobs: uses: actions/checkout@v4 - name: 📥 Checkout PR - uses: goat-community/.github/.github/actions/pr-git-checkout + uses: goat-community/.github/.github/actions/pr-git-checkout@main - name: 📥 Monorepo install - uses: goat-community/.github/.github/actions/pnpm-install + uses: goat-community/.github/.github/actions/pnpm-install@main with: enable-corepack: true cwd: ${{ github.workspace }} diff --git a/apps/goat/app/[lng]/map/[projectId]/page.tsx b/apps/goat/app/[lng]/map/[projectId]/page.tsx index cc286fbd..e367a821 100644 --- a/apps/goat/app/[lng]/map/[projectId]/page.tsx +++ b/apps/goat/app/[lng]/map/[projectId]/page.tsx @@ -21,25 +21,25 @@ import React, { useCallback, useEffect, useRef, useState } from "react"; import Map, { MapProvider, Layer, Source } from "react-map-gl"; import type { CSSObject } from "tss-react"; import Layers from "@/components/map/Layers"; - import { ICON_NAME } from "@p4b/ui/components/Icon"; import { Fullscren } from "@/components/map/controls/Fullscreen"; import Geocoder from "@/components/map/controls/Geocoder"; import { useSelector } from "react-redux"; import type { IStore } from "@/types/store"; -import { setActiveBasemapIndex } from "@/lib/store/styling/slice"; +import { setActiveBasemapIndex, setIcon } from "@/lib/store/styling/slice"; import MapStyle from "@/components/map/panels/mapStyle/MapStyle"; import { fetchLayerData } from "@/lib/store/styling/actions"; import { useAppDispatch } from "@/hooks/useAppDispatch"; -import { selectMapLayer } from '@/lib/store/styling/selectors' +import { selectMapLayer } from "@/lib/store/styling/selectors"; const sidebarWidth = 48; const toolbarHeight = 52; export default function MapPage({ params: { projectId } }) { - const { basemaps, activeBasemapIndex, initialViewState } = - useSelector((state: IStore) => state.styling); - const mapLayer = useSelector(selectMapLayer) + const { basemaps, activeBasemapIndex, initialViewState } = useSelector( + (state: IStore) => state.styling, + ); + const mapLayer = useSelector(selectMapLayer); const [activeLeft, setActiveLeft] = useState( undefined, @@ -272,6 +272,40 @@ export default function MapPage({ params: { projectId } }) { mapStyle={basemaps[activeBasemapIndex[0]].url} attributionControl={false} mapboxAccessToken={MAPBOX_TOKEN} + onLoad={(e) => { + const changeIcon = (src: string) => { + const map = e.target + + const newImage = new Image(); + newImage.crossOrigin = "Anonymous"; + newImage.src = src; + + const xhr = new XMLHttpRequest(); + + xhr.open('GET', src, true) + xhr.onreadystatechange = () => { + if(xhr.readyState === 4 && xhr.status === 200) { + + newImage.src = `data:image/svg+xml,${encodeURIComponent(xhr.responseText)}` + + } + } + + xhr.send() + + newImage.onload = () => { + if (map.hasImage("dentist-15")) { + map.removeImage('dentist-15'); + map.addImage("dentist-15", newImage, {sdf: true}); + } + } + } + + + + dispatch(setIcon(changeIcon)) + + }} > {mapLayer ? ( - + ) : null} {/* todo check */} diff --git a/apps/goat/components/map/panels/mapStyle/ColorOptionSymbol.tsx b/apps/goat/components/map/panels/mapStyle/ColorOptionSymbol.tsx index 8c53decb..ca9dba4c 100644 --- a/apps/goat/components/map/panels/mapStyle/ColorOptionSymbol.tsx +++ b/apps/goat/components/map/panels/mapStyle/ColorOptionSymbol.tsx @@ -4,7 +4,7 @@ import Box from "@p4b/ui/components/Box"; import { Divider, TextField, Typography } from "@mui/material"; import React from "react"; import { useSelector } from "react-redux"; -import { setLayerFillOutLineColor } from "@/lib/store/styling/slice"; +import { setIconFillColor, setLayerFillOutLineColor } from "@/lib/store/styling/slice"; import { useAppDispatch } from "@/hooks/useAppDispatch"; import { selectMapLayer } from "@/lib/store/styling/selectors"; @@ -17,7 +17,7 @@ const ColorOptionSymbol = () => { event: React.ChangeEvent ) => { console.log("value", event.target.value); - // dispatch(setLayerFillColor(event.target.value)); + dispatch(setIconFillColor(event.target.value)); }; const handleStrokeColorChange = ( @@ -48,7 +48,7 @@ const ColorOptionSymbol = () => { type="color" size="small" className={classes.inputs} - value={mapLayer?.paint?.["fill-color"]} + value={mapLayer?.paint?.["icon-color"]} onChange={handleFillColorChange} /> {/* { + const { changeIcon } = useSelector((state: IStore) => state.styling); const { classes } = useStyles(); const theme = useTheme(); + const [value, setValue] = useState("icon"); + return ( - { + setValue(e.target.value); + }} + className={classes.select} + > { + {value === "icon" && ( +
+ changeIcon?.(star.src)} + /> + changeIcon?.(circle.src)} + /> +
+ )}
); }; diff --git a/apps/goat/components/map/panels/mapStyle/SizeOptionSymbol.tsx b/apps/goat/components/map/panels/mapStyle/SizeOptionSymbol.tsx index fe5950a2..f84f62a9 100644 --- a/apps/goat/components/map/panels/mapStyle/SizeOptionSymbol.tsx +++ b/apps/goat/components/map/panels/mapStyle/SizeOptionSymbol.tsx @@ -1,16 +1,21 @@ -import React, { useState } from "react"; import BasicAccordion from "@p4b/ui/components/BasicAccordion"; import { Slider, TextField } from "@mui/material"; import { makeStyles } from "@/lib/theme"; import Box from "@p4b/ui/components/Box"; +import { useSelector } from "react-redux"; +import { selectMapLayer } from "@/lib/store/styling/selectors"; +import { useAppDispatch } from "@/hooks/useAppDispatch"; +import { setLayerSymbolSize } from "@/lib/store/styling/slice"; +import React from "react"; const SizeOptionSymbol = () => { - const [value, setValue] = useState(20); + const mapLayer = useSelector(selectMapLayer); + const dispatch = useAppDispatch(); const { classes } = useStyles(); const handleSliderChange = (_: Event, newValue: number | number[]) => { if (typeof newValue === "number") { - setValue(newValue); + dispatch(setLayerSymbolSize({ val: newValue })); } }; @@ -19,7 +24,7 @@ const SizeOptionSymbol = () => { ) => { const newValue = parseFloat(event.target.value); if (!isNaN(newValue)) { - setValue(newValue); + dispatch(setLayerSymbolSize({ val: newValue })); } }; @@ -30,16 +35,24 @@ const SizeOptionSymbol = () => { diff --git a/apps/goat/lib/store/index.ts b/apps/goat/lib/store/index.ts index f37dbba8..b48b4cd2 100644 --- a/apps/goat/lib/store/index.ts +++ b/apps/goat/lib/store/index.ts @@ -9,6 +9,9 @@ const store = configureStore({ styling: stylingReducer, mapFilters: filtersReducer, }, + middleware: (getDefaultMiddleware) => getDefaultMiddleware({ + serializableCheck: false + }) }); export type AppDispatch = typeof store.dispatch; diff --git a/apps/goat/lib/store/styling/slice.ts b/apps/goat/lib/store/styling/slice.ts index e88172a8..63be6cba 100644 --- a/apps/goat/lib/store/styling/slice.ts +++ b/apps/goat/lib/store/styling/slice.ts @@ -1,7 +1,7 @@ import type { PayloadAction } from "@reduxjs/toolkit"; import { createSlice } from "@reduxjs/toolkit"; import { fetchLayerData } from "@/lib/store/styling/actions"; -import type { AnyLayer } from 'react-map-gl' +import type { AnyLayer } from "react-map-gl"; interface IViewState { latitude: number; @@ -28,12 +28,16 @@ interface IMarker { iconName: string; } -export type TLayer = AnyLayer & { sources: { - composite: { - url: string - type: string - } -} } | null +export type TLayer = + | (AnyLayer & { + sources: { + composite: { + url: string; + type: string; + }; + }; + }) + | null; export interface IStylingState { initialViewState: IViewState; @@ -42,6 +46,7 @@ export interface IStylingState { activeBasemapIndex: number[]; markers: IMarker[]; mapLayer: unknown; + changeIcon: null | ((src: string) => void); } const initialState: IStylingState = { @@ -96,12 +101,16 @@ const initialState: IStylingState = { markers: [], //todo need get layer from db mapLayer: null as TLayer, + changeIcon: null }; const stylingSlice = createSlice({ name: "styling", initialState, reducers: { + setIcon: (state, action: PayloadAction<(src: string) => void>) => { + state.changeIcon = action.payload + }, setTabValue: (state, action: PayloadAction) => { state.tabValue = action.payload; }, @@ -131,10 +140,10 @@ const stylingSlice = createSlice({ state, action: PayloadAction<{ key: string; val: string }>, ) => { - const mapLayer = state.mapLayer as TLayer - + const mapLayer = state.mapLayer as TLayer; + if (mapLayer) { - mapLayer.paint = mapLayer.paint ?? {} + mapLayer.paint = mapLayer.paint ?? {}; mapLayer.paint[action.payload.key] = action.payload.val; } }, @@ -142,22 +151,22 @@ const stylingSlice = createSlice({ state, action: PayloadAction<{ key: string; val: number }>, ) => { - const mapLayer = state.mapLayer as TLayer + const mapLayer = state.mapLayer as TLayer; if (mapLayer) { - mapLayer.paint = mapLayer.paint ?? {} + mapLayer.paint = mapLayer.paint ?? {}; mapLayer.paint[action.payload.key] = action.payload.val; } }, setLayerFillOutLineColor: (state, action: PayloadAction) => { - const mapLayer = state.mapLayer as TLayer + const mapLayer = state.mapLayer as TLayer; if (mapLayer?.paint) { mapLayer.paint["fill-outline-color"] = action.payload; } }, deleteLayerFillOutLineColor: (state) => { - const mapLayer = state.mapLayer as TLayer + const mapLayer = state.mapLayer as TLayer; if (mapLayer?.paint) { if ("fill-outline-color" in mapLayer.paint) { @@ -165,6 +174,24 @@ const stylingSlice = createSlice({ } } }, + setLayerSymbolSize: ( + state, + action: PayloadAction<{ val: number }>, + ) => { + const mapLayer = state.mapLayer as TLayer; + + if (mapLayer) { + mapLayer.layout = mapLayer.layout ?? {}; + mapLayer.layout['icon-size'] = action.payload.val; + } + }, + setIconFillColor: (state, action: PayloadAction) => { + const mapLayer = state.mapLayer as TLayer; + + if (mapLayer?.paint) { + mapLayer.paint["icon-color"] = action.payload; + } + } // setLayerIconImage: (state, action: PayloadAction) => { // state.mapLayer.layers[0].layout["icon-image"] = action.payload; // }, @@ -186,6 +213,8 @@ const stylingSlice = createSlice({ }); export const { + setIconFillColor, + setIcon, setTabValue, setActiveBasemapIndex, addMarker, @@ -194,6 +223,7 @@ export const { saveStyles, setLayerLineWidth, deleteLayerFillOutLineColor, + setLayerSymbolSize, } = stylingSlice.actions; export const stylingReducer = stylingSlice.reducer; diff --git a/apps/goat/lib/utils/mockLayerData.js b/apps/goat/lib/utils/mockLayerData.js index 91cfa4b4..5f0cdeca 100644 --- a/apps/goat/lib/utils/mockLayerData.js +++ b/apps/goat/lib/utils/mockLayerData.js @@ -9,21 +9,13 @@ export const stylesObj = { }, id: "cljxz3bl1003v01qy7k5m0apj", type: "symbol", - paint: {}, + paint: { + 'icon-color': '#316940', + }, source: "composite", "source-layer": "poi", layout: { - "icon-image": [ - "match", - ["get", "category"], - ["dentist"], - "dentist-15", - ["bakery"], - "bakery-11", - ["nursery"], - "hospital-15", - "", - ], + "icon-image": "dentist-15", }, }, aoi: { diff --git a/apps/goat/public/assets/poi-icons/circle.svg b/apps/goat/public/assets/poi-icons/circle.svg new file mode 100644 index 00000000..468fadfd --- /dev/null +++ b/apps/goat/public/assets/poi-icons/circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/goat/public/assets/poi-icons/star.svg b/apps/goat/public/assets/poi-icons/star.svg new file mode 100644 index 00000000..f9f260e7 --- /dev/null +++ b/apps/goat/public/assets/poi-icons/star.svg @@ -0,0 +1,3 @@ + + +