diff --git a/cypress/e2e/item/duplicate/duplicateItem.cy.ts b/cypress/e2e/item/duplicate/duplicateItem.cy.ts
index 2f25757b7..c21698af4 100644
--- a/cypress/e2e/item/duplicate/duplicateItem.cy.ts
+++ b/cypress/e2e/item/duplicate/duplicateItem.cy.ts
@@ -6,7 +6,7 @@ import { SAMPLE_ITEMS } from '../../../fixtures/items';
import duplicateItem from '../../../support/actionsUtils';
describe('duplicate Item in Home', () => {
- Object.values(ItemLayoutMode).forEach((view) => {
+ Object.values([ItemLayoutMode.Grid, ItemLayoutMode.List]).forEach((view) => {
it(`duplicate item on Home in ${view} view`, () => {
cy.setUpApi(SAMPLE_ITEMS);
cy.visit(HOME_PATH);
@@ -25,7 +25,7 @@ describe('duplicate Item in Home', () => {
});
});
describe('duplicate Item in item', () => {
- Object.values(ItemLayoutMode).forEach((view) => {
+ Object.values([ItemLayoutMode.Grid, ItemLayoutMode.List]).forEach((view) => {
it(`duplicate item in item in ${view} view`, () => {
cy.setUpApi(SAMPLE_ITEMS);
const { id, path } = SAMPLE_ITEMS.items[0];
diff --git a/package.json b/package.json
index 5207041d5..be291cb32 100644
--- a/package.json
+++ b/package.json
@@ -20,10 +20,11 @@
"@emotion/react": "11.11.4",
"@emotion/styled": "11.11.0",
"@graasp/chatbox": "3.1.0",
- "@graasp/query-client": "2.8.0",
+ "@graasp/map": "1.4.0",
+ "@graasp/query-client": "2.9.0",
"@graasp/sdk": "4.1.0",
"@graasp/translations": "1.25.2",
- "@graasp/ui": "4.9.0",
+ "@graasp/ui": "4.9.1",
"@mui/icons-material": "5.15.11",
"@mui/lab": "5.0.0-alpha.166",
"@mui/material": "5.15.11",
diff --git a/src/components/item/MapView.tsx b/src/components/item/MapView.tsx
new file mode 100644
index 000000000..346d0e5f6
--- /dev/null
+++ b/src/components/item/MapView.tsx
@@ -0,0 +1,45 @@
+import { Typography } from '@mui/material';
+
+import { Map } from '@graasp/map';
+import { type DiscriminatedItem, redirect } from '@graasp/sdk';
+
+import { buildGraaspPlayerView } from '@/config/externalPaths';
+import { hooks, mutations } from '@/config/queryClient';
+import { buildPlayerTabName } from '@/config/selectors';
+
+type Props = {
+ parentId?: DiscriminatedItem['id'];
+ title: string;
+};
+
+const MapView = ({ parentId, title }: Props): JSX.Element => {
+ const { data: currentMember } = hooks.useCurrentMember();
+
+ return (
+ <>
+
+ {title}
+
+
+
+ >
+ );
+};
+
+export default MapView;
diff --git a/src/components/item/header/ModeButton.tsx b/src/components/item/header/ModeButton.tsx
index b80fe3863..95dd9b30c 100644
--- a/src/components/item/header/ModeButton.tsx
+++ b/src/components/item/header/ModeButton.tsx
@@ -4,7 +4,7 @@ import {
List as ListIcon,
ViewModule as ViewModuleIcon,
} from '@mui/icons-material';
-// import MapIcon from '@mui/icons-material/Map';
+import MapIcon from '@mui/icons-material/Map';
import { IconButton } from '@mui/material';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
@@ -16,8 +16,8 @@ import { useLayoutContext } from '../../context/LayoutContext';
const modeToIcon = (mode: ItemLayoutMode) => {
switch (mode) {
- // case ItemLayoutMode.Map:
- // return ;
+ case ItemLayoutMode.Map:
+ return ;
case ItemLayoutMode.Grid:
return ;
case ItemLayoutMode.List:
diff --git a/src/components/item/settings/GeolocationPicker.tsx b/src/components/item/settings/GeolocationPicker.tsx
index 80f8e7558..da32cd82d 100644
--- a/src/components/item/settings/GeolocationPicker.tsx
+++ b/src/components/item/settings/GeolocationPicker.tsx
@@ -1,27 +1,16 @@
-import { ChangeEventHandler } from 'react';
-
import Clear from '@mui/icons-material/Clear';
-import {
- Box,
- IconButton,
- InputAdornment,
- LinearProgress,
- List,
- ListItemButton,
- OutlinedInput,
- Stack,
- Tooltip,
- Typography,
-} from '@mui/material';
+import { IconButton, Stack, Tooltip, Typography } from '@mui/material';
+import {
+ type GeolocationPickerProps,
+ GeolocationPicker as MapGeolocationPicker,
+} from '@graasp/map';
import { DiscriminatedItem } from '@graasp/sdk';
import { useBuilderTranslation } from '@/config/i18n';
import { hooks, mutations } from '@/config/queryClient';
import { BUILDER } from '@/langs/constants';
-import { OpenStreetMapResult, useSearchAddress } from './hooks';
-
const GeolocationPicker = ({
item,
}: {
@@ -32,42 +21,26 @@ const GeolocationPicker = ({
const { mutate: putGeoloc } = mutations.usePutItemGeolocation();
const { mutate: deleteGeoloc } = mutations.useDeleteItemGeolocation();
- const {
- query,
- setQuery,
- isDebounced,
- setResults,
- results,
- loading,
- clearQuery,
- } = useSearchAddress({
- lang: item.lang,
- geoloc,
- });
-
- const onChange: ChangeEventHandler = (e) => {
- setQuery(e.target.value);
- };
-
- const onChangeOption = (option: OpenStreetMapResult): void => {
- const {
- raw: { lat, lon: lng },
- label,
- } = option;
+ const onChangeOption: GeolocationPickerProps['onChangeOption'] = (option: {
+ addressLabel: string;
+ lat: number;
+ lng: number;
+ country?: string;
+ }): void => {
+ const { addressLabel, lat, lng, country } = option;
putGeoloc({
itemId: item.id,
geolocation: {
- lng: parseFloat(lng),
- lat: parseFloat(lat),
- addressLabel: label,
+ addressLabel,
+ lat,
+ lng,
+ country,
},
});
- setResults([]);
};
const clearGeoloc = () => {
deleteGeoloc({ itemId: item.id });
- clearQuery();
};
// the input is disabled if the geoloc is defined in parent
@@ -86,30 +59,18 @@ const GeolocationPicker = ({
-
-
-
-
-
-
-
- ) : undefined
- }
+
- {loading && (
-
-
-
- )}
+
+
+
+
+
+
+
{isDisabled && (
@@ -117,26 +78,6 @@ const GeolocationPicker = ({
{t(BUILDER.ITEM_SETTINGS_GEOLOCATION_INHERITED_EXPLANATION)}
)}
-
- {results && (
-
- {results.map((r) => (
- onChangeOption(r)}
- >
- {r.label}
-
- ))}
- {!results.length &&
- query &&
- query !== geoloc?.addressLabel &&
- !loading &&
- !isDebounced &&
- t(BUILDER.ITEM_SETTINGS_GEOLOCATION_NO_ADDRESS)}
-
- )}
-
);
};
diff --git a/src/components/item/settings/hooks.ts b/src/components/item/settings/hooks.ts
deleted file mode 100644
index d6c0ae0fe..000000000
--- a/src/components/item/settings/hooks.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { Dispatch, useCallback, useEffect, useMemo, useState } from 'react';
-
-import { ItemGeolocation } from '@graasp/sdk';
-
-import { OpenStreetMapProvider } from 'leaflet-geosearch';
-
-import useDebouncedCallback from '@/utils/useDebounce';
-
-type Props = {
- geoloc?: ItemGeolocation | null;
- lang: string;
-};
-
-const DELAY_MS = 1000;
-
-export type OpenStreetMapResult = Awaited<
- ReturnType
->[0];
-
-type ReturnedValue = {
- clearQuery: () => void;
- isDebounced: boolean;
- loading: boolean;
- query?: string | null;
- results: OpenStreetMapResult[];
- setQuery: Dispatch;
- setResults: Dispatch;
-};
-
-// eslint-disable-next-line import/prefer-default-export
-export const useSearchAddress = ({ lang, geoloc }: Props): ReturnedValue => {
- const [results, setResults] = useState([]);
- const [loading, setLoading] = useState(false);
- const [query, setQuery] = useState();
-
- const provider = useMemo(
- () =>
- new OpenStreetMapProvider({
- params: {
- 'accept-language': lang,
- },
- }),
- [lang],
- );
-
- useEffect(() => {
- setQuery(geoloc?.addressLabel);
- }, [geoloc]);
-
- const callback = useCallback(() => {
- if (query && query !== geoloc?.addressLabel) {
- setLoading(true);
- provider
- .search({ query })
- .then((e) => {
- setResults(e);
- })
- .finally(() => {
- setLoading(false);
- });
- }
- }, [query, geoloc?.addressLabel, provider]);
-
- const clearQuery = () => {
- setQuery('');
- };
-
- const { isDebounced } = useDebouncedCallback(callback, DELAY_MS);
-
- return {
- results,
- query,
- setQuery,
- isDebounced,
- setResults,
- loading,
- clearQuery,
- };
-};
diff --git a/src/components/main/Items.tsx b/src/components/main/Items.tsx
index aa7ad9028..4407e75f7 100644
--- a/src/components/main/Items.tsx
+++ b/src/components/main/Items.tsx
@@ -5,6 +5,7 @@ import { ShowOnlyMeChangeType } from '@/config/types';
import { hooks } from '../../config/queryClient';
import { ItemLayoutMode } from '../../enums';
import { useLayoutContext } from '../context/LayoutContext';
+import MapView from '../item/MapView';
import { useItemsStatuses } from '../table/BadgesCellRenderer';
import ItemsGrid from './ItemsGrid';
import ItemsTable from './ItemsTable';
@@ -75,6 +76,8 @@ const Items = ({
itemsTags,
});
switch (mode) {
+ case ItemLayoutMode.Map:
+ return ;
case ItemLayoutMode.Grid:
return (
=5.15.0"
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ react-dom: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ "@types/react":
+ optional: true
+ checksum: 10/b5e5dc242f850819516f5e6108769bf496086a41b7e59fe2bd98e3dcd682eed551f4846ca48c6b9f31b659ee2a8360d77098bd9b2785fd204d82f6d2d91ea156
+ languageName: node
+ linkType: hard
+
"@mui/material@npm:5.15.11":
version: 5.15.11
resolution: "@mui/material@npm:5.15.11"
@@ -1794,6 +1916,39 @@ __metadata:
languageName: node
linkType: hard
+"@mui/material@npm:5.15.12":
+ version: 5.15.12
+ resolution: "@mui/material@npm:5.15.12"
+ dependencies:
+ "@babel/runtime": "npm:^7.23.9"
+ "@mui/base": "npm:5.0.0-beta.38"
+ "@mui/core-downloads-tracker": "npm:^5.15.12"
+ "@mui/system": "npm:^5.15.12"
+ "@mui/types": "npm:^7.2.13"
+ "@mui/utils": "npm:^5.15.12"
+ "@types/react-transition-group": "npm:^4.4.10"
+ clsx: "npm:^2.1.0"
+ csstype: "npm:^3.1.3"
+ prop-types: "npm:^15.8.1"
+ react-is: "npm:^18.2.0"
+ react-transition-group: "npm:^4.4.5"
+ peerDependencies:
+ "@emotion/react": ^11.5.0
+ "@emotion/styled": ^11.3.0
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ react-dom: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ "@types/react":
+ optional: true
+ checksum: 10/adee362d8367c4dec08fd4cec6b41fbe05699290b98e334c0203a73a4a13c24f2f4387b7f54b2000704b33182c3704fe873222227ccb58cc2099f9124bf02763
+ languageName: node
+ linkType: hard
+
"@mui/private-theming@npm:^5.15.11":
version: 5.15.11
resolution: "@mui/private-theming@npm:5.15.11"
@@ -1811,6 +1966,23 @@ __metadata:
languageName: node
linkType: hard
+"@mui/private-theming@npm:^5.15.12":
+ version: 5.15.12
+ resolution: "@mui/private-theming@npm:5.15.12"
+ dependencies:
+ "@babel/runtime": "npm:^7.23.9"
+ "@mui/utils": "npm:^5.15.12"
+ prop-types: "npm:^15.8.1"
+ peerDependencies:
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 10/9e4e144d3de79536ec7cb46c98cabbe334ec7368c49ec3aa5a312e37fb8571cc95bad60b799e3252bbce7f2de6cc467f250cb217ca60316ead5303b5f5f2a660
+ languageName: node
+ linkType: hard
+
"@mui/styled-engine@npm:^5.15.11":
version: 5.15.11
resolution: "@mui/styled-engine@npm:5.15.11"
@@ -1860,6 +2032,34 @@ __metadata:
languageName: node
linkType: hard
+"@mui/system@npm:^5.15.12":
+ version: 5.15.12
+ resolution: "@mui/system@npm:5.15.12"
+ dependencies:
+ "@babel/runtime": "npm:^7.23.9"
+ "@mui/private-theming": "npm:^5.15.12"
+ "@mui/styled-engine": "npm:^5.15.11"
+ "@mui/types": "npm:^7.2.13"
+ "@mui/utils": "npm:^5.15.12"
+ clsx: "npm:^2.1.0"
+ csstype: "npm:^3.1.3"
+ prop-types: "npm:^15.8.1"
+ peerDependencies:
+ "@emotion/react": ^11.5.0
+ "@emotion/styled": ^11.3.0
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ "@types/react":
+ optional: true
+ checksum: 10/23642eea1f5948cf0c57674569af9df3e7630d888da80d5d34afe6259a21631424f86b23a431a76a4046a6caa738d81d8a1b36ca438c6695aa93ca62b08afd50
+ languageName: node
+ linkType: hard
+
"@mui/types@npm:^7.2.13":
version: 7.2.13
resolution: "@mui/types@npm:7.2.13"
@@ -1890,6 +2090,24 @@ __metadata:
languageName: node
linkType: hard
+"@mui/utils@npm:^5.15.12":
+ version: 5.15.12
+ resolution: "@mui/utils@npm:5.15.12"
+ dependencies:
+ "@babel/runtime": "npm:^7.23.9"
+ "@types/prop-types": "npm:^15.7.11"
+ prop-types: "npm:^15.8.1"
+ react-is: "npm:^18.2.0"
+ peerDependencies:
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 10/e4c40d73a0367ccc89491835fb98baafaea046fbcdd46d407689b8cda95ead1d45bb3b4364a135c1fa20d1c3b23ad69705e228943e5995d597860be9b5ab8c2b
+ languageName: node
+ linkType: hard
+
"@nodelib/fs.scandir@npm:2.1.5":
version: 2.1.5
resolution: "@nodelib/fs.scandir@npm:2.1.5"
@@ -1953,6 +2171,17 @@ __metadata:
languageName: node
linkType: hard
+"@react-leaflet/core@npm:^2.1.0":
+ version: 2.1.0
+ resolution: "@react-leaflet/core@npm:2.1.0"
+ peerDependencies:
+ leaflet: ^1.9.0
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ checksum: 10/12ce28b85cf6712a1a7b7c49466b941fc619bc7b1535308bc5711a35f7e89eb16298babfd62f6b3a92e64abf94dcf517b2bc460f59fcf20599821bc6ab3b3048
+ languageName: node
+ linkType: hard
+
"@remix-run/router@npm:1.15.2":
version: 1.15.2
resolution: "@remix-run/router@npm:1.15.2"
@@ -1960,6 +2189,13 @@ __metadata:
languageName: node
linkType: hard
+"@remix-run/router@npm:1.15.3":
+ version: 1.15.3
+ resolution: "@remix-run/router@npm:1.15.3"
+ checksum: 10/43d402b4ad3dff6dee5c1bc0822aeeb4d885d11c74c45fca7f2f4d7e57853fddbbb813c372919bb3fcc63f95fbcffdd1d4ac1c406857ea07b9d09a09d0562c8e
+ languageName: node
+ linkType: hard
+
"@rollup/pluginutils@npm:^5.0.2":
version: 5.1.0
resolution: "@rollup/pluginutils@npm:5.1.0"
@@ -7231,10 +7467,11 @@ __metadata:
"@emotion/react": "npm:11.11.4"
"@emotion/styled": "npm:11.11.0"
"@graasp/chatbox": "npm:3.1.0"
- "@graasp/query-client": "npm:2.8.0"
+ "@graasp/map": "npm:1.4.0"
+ "@graasp/query-client": "npm:2.9.0"
"@graasp/sdk": "npm:4.1.0"
"@graasp/translations": "npm:1.25.2"
- "@graasp/ui": "npm:4.9.0"
+ "@graasp/ui": "npm:4.9.1"
"@mui/icons-material": "npm:5.15.11"
"@mui/lab": "npm:5.0.0-alpha.166"
"@mui/material": "npm:5.15.11"
@@ -7611,6 +7848,15 @@ __metadata:
languageName: node
linkType: hard
+"i18next@npm:23.7.16":
+ version: 23.7.16
+ resolution: "i18next@npm:23.7.16"
+ dependencies:
+ "@babel/runtime": "npm:^7.23.2"
+ checksum: 10/77e74c07a73316f6fb6678a5a3e8ce58a6e66be457dd1ccd23941e9fc57ad8e1da55193fa6328c70b86073337b776cd267f3c13c6309f548b3116f27a1e41787
+ languageName: node
+ linkType: hard
+
"iconv-lite@npm:0.4.24":
version: 0.4.24
resolution: "iconv-lite@npm:0.4.24"
@@ -8668,6 +8914,15 @@ __metadata:
languageName: node
linkType: hard
+"leaflet-easybutton@npm:2.4.0":
+ version: 2.4.0
+ resolution: "leaflet-easybutton@npm:2.4.0"
+ dependencies:
+ leaflet: "npm:^1.0.1"
+ checksum: 10/02c94dcfa19344d33908b329fa2e4531dfbebc97bdaa0b22fe0f9872ff8f5173ef28da54f7c5ea0ab38cbda71305107c3d7938e141e1029e8fdc5149cfe8d353
+ languageName: node
+ linkType: hard
+
"leaflet-geosearch@npm:3.11.1":
version: 3.11.1
resolution: "leaflet-geosearch@npm:3.11.1"
@@ -8683,7 +8938,7 @@ __metadata:
languageName: node
linkType: hard
-"leaflet@npm:^1.6.0":
+"leaflet@npm:^1.0.1, leaflet@npm:^1.6.0, leaflet@npm:^1.9.4":
version: 1.9.4
resolution: "leaflet@npm:1.9.4"
checksum: 10/7b6a74d503980961a85bdabebf9d1162c26db0e88195800ceea311682b653d621718f2ada3c9aab903a735af9862c9ae278ba550d4429acbd954d43449cd0d77
@@ -11012,7 +11267,7 @@ __metadata:
languageName: node
linkType: hard
-"react-dom@npm:18.2.0":
+"react-dom@npm:18.2.0, react-dom@npm:^18.2.0":
version: 18.2.0
resolution: "react-dom@npm:18.2.0"
dependencies:
@@ -11076,6 +11331,24 @@ __metadata:
languageName: node
linkType: hard
+"react-i18next@npm:14.0.8":
+ version: 14.0.8
+ resolution: "react-i18next@npm:14.0.8"
+ dependencies:
+ "@babel/runtime": "npm:^7.23.9"
+ html-parse-stringify: "npm:^3.0.1"
+ peerDependencies:
+ i18next: ">= 23.2.3"
+ react: ">= 16.8.0"
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ checksum: 10/5481cafa322476663e339d6eed2e4fb13e14fcb575bce9fc205bbc71da6d3a3392a1377d7eb59371357d14994b147d36214ec319ca2f7d85cd3526abfda54705
+ languageName: node
+ linkType: hard
+
"react-image-crop@npm:11.0.5":
version: 11.0.5
resolution: "react-image-crop@npm:11.0.5"
@@ -11113,6 +11386,19 @@ __metadata:
languageName: node
linkType: hard
+"react-leaflet@npm:^4.2.1":
+ version: 4.2.1
+ resolution: "react-leaflet@npm:4.2.1"
+ dependencies:
+ "@react-leaflet/core": "npm:^2.1.0"
+ peerDependencies:
+ leaflet: ^1.9.0
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ checksum: 10/01cee12dc32e86d0153c989894fdba1c5c50fa41ad8d712352fa616f7b0dd32844aa17bb06c66f7569133675e2946c669090b4c496610cce3fa37c22254ca89f
+ languageName: node
+ linkType: hard
+
"react-markdown@npm:^9.0.1":
version: 9.0.1
resolution: "react-markdown@npm:9.0.1"
@@ -11252,6 +11538,19 @@ __metadata:
languageName: node
linkType: hard
+"react-router-dom@npm:6.22.3":
+ version: 6.22.3
+ resolution: "react-router-dom@npm:6.22.3"
+ dependencies:
+ "@remix-run/router": "npm:1.15.3"
+ react-router: "npm:6.22.3"
+ peerDependencies:
+ react: ">=16.8"
+ react-dom: ">=16.8"
+ checksum: 10/868a530c3167e1903f170818c0162760b8fbe9b10a7a7a79e5998990df341cd7127ba7819af4a9105af72c13453c7c4d76b2b07a70b56fff012fa0508b51940e
+ languageName: node
+ linkType: hard
+
"react-router@npm:6.22.2":
version: 6.22.2
resolution: "react-router@npm:6.22.2"
@@ -11263,6 +11562,17 @@ __metadata:
languageName: node
linkType: hard
+"react-router@npm:6.22.3":
+ version: 6.22.3
+ resolution: "react-router@npm:6.22.3"
+ dependencies:
+ "@remix-run/router": "npm:1.15.3"
+ peerDependencies:
+ react: ">=16.8"
+ checksum: 10/df3948afd6500faf4b82a72375b9177536d878d54cad18e20a175efcbfdd0d94852aac59660d786946636ed325284d94a8f46652d898df304d6a29c9a3932afd
+ languageName: node
+ linkType: hard
+
"react-text-mask@npm:5.5.0":
version: 5.5.0
resolution: "react-text-mask@npm:5.5.0"
@@ -11301,7 +11611,7 @@ __metadata:
languageName: node
linkType: hard
-"react@npm:18.2.0":
+"react@npm:18.2.0, react@npm:^18.2.0":
version: 18.2.0
resolution: "react@npm:18.2.0"
dependencies: