diff --git a/cypress/e2e/item/create/createDocument.cy.ts b/cypress/e2e/item/create/createDocument.cy.ts index 829634607..0203c77bf 100644 --- a/cypress/e2e/item/create/createDocument.cy.ts +++ b/cypress/e2e/item/create/createDocument.cy.ts @@ -4,13 +4,7 @@ import { PackedFolderItemFactory, } from '@graasp/sdk'; -import { - CREATE_ITEM_BUTTON_ID, - CREATE_ITEM_DOCUMENT_ID, - ITEM_FORM_CONFIRM_BUTTON_ID, - ITEM_FORM_DISPLAY_NAME_INPUT_ID, - ITEM_FORM_NAME_INPUT_ID, -} from '@/config/selectors'; +import { ITEM_FORM_CONFIRM_BUTTON_ID } from '@/config/selectors'; import { HOME_PATH, buildItemPath } from '../../../../src/config/paths'; import ItemLayoutMode from '../../../../src/enums/itemLayoutMode'; @@ -74,19 +68,4 @@ describe('Create Document', () => { true, ); }); - - it('autoComplete document displayName', () => { - cy.setUpApi(); - cy.visit(HOME_PATH); - - cy.switchMode(ItemLayoutMode.List); - - cy.get(`#${CREATE_ITEM_BUTTON_ID}`).click(); - cy.get(`#${CREATE_ITEM_DOCUMENT_ID}`).click(); - cy.get(`#${ITEM_FORM_NAME_INPUT_ID}`).type('Test Name'); - cy.get(`#${ITEM_FORM_DISPLAY_NAME_INPUT_ID}`).should( - 'have.value', - 'Test Name', - ); - }); }); diff --git a/cypress/fixtures/links.ts b/cypress/fixtures/links.ts index df0637f5b..e71f910a7 100644 --- a/cypress/fixtures/links.ts +++ b/cypress/fixtures/links.ts @@ -55,6 +55,10 @@ export const YOUTUBE_LINK_ITEM: LinkItemType = PackedLinkItemFactory({ thumbnails: ['https://i.ytimg.com/vi/FmiEgBMTPLo/maxresdefault.jpg'], icons: ['https://www.youtube.com/s/desktop/f0ff6c1d/img/favicon_96.png'], }), + settings: { + // this is necessary for Youtube to show the embed + showLinkIframe: true, + }, }); export const INVALID_LINK_ITEM: LinkItemType = PackedLinkItemFactory({ diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index f1d3ff5c8..7f47f6068 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -54,6 +54,7 @@ import { mockGetItems, mockGetItemsTags, mockGetLatestValidationGroup, + mockGetLinkMetadata, mockGetManyPublishItemInformations, mockGetMember, mockGetMemberMentions, @@ -341,6 +342,8 @@ Cypress.Commands.add( mockPatchShortLink(cachedShortLinks, patchShortLinkError); mockDeleteShortLink(cachedShortLinks, deleteShortLinkError); + + mockGetLinkMetadata(); }, ); diff --git a/cypress/support/server.ts b/cypress/support/server.ts index 9ed69e920..294fe7d40 100644 --- a/cypress/support/server.ts +++ b/cypress/support/server.ts @@ -2195,3 +2195,29 @@ export const mockDeleteShortLink = ( }, ).as('deleteShortLink'); }; + +export const mockGetLinkMetadata = (): void => { + cy.intercept( + { + method: HttpMethod.Get, + url: new RegExp(`${API_HOST}/items/embedded-links/metadata*`), + }, + ({ reply, url }) => { + let linkUrl = new URL(url).searchParams.get('link'); + + if (!linkUrl.includes('http')) { + linkUrl = `https://${linkUrl}`; + } + if (URL.canParse(linkUrl)) { + return reply({ + title: 'Page title', + description: 'Page description', + html: '', + icons: [], + thumbnails: [], + }); + } + return reply({ statusCode: StatusCodes.BAD_REQUEST }); + }, + ).as('getLinkMetadata'); +}; diff --git a/cypress/support/viewUtils.ts b/cypress/support/viewUtils.ts index 752973913..c7636cceb 100644 --- a/cypress/support/viewUtils.ts +++ b/cypress/support/viewUtils.ts @@ -108,7 +108,8 @@ export const expectLinkViewScreenLayout = ({ } if (!html && (settings?.showLinkButton ?? DEFAULT_LINK_SHOW_BUTTON)) { - cy.get('[data-testid="OpenInNewIcon"]').should('be.visible'); + // this data-testid is set in graasp/ui + cy.get('[data-testid="fancy-link-card"]').should('be.visible'); } if (description) { diff --git a/package.json b/package.json index e8c12e8b4..223e00200 100644 --- a/package.json +++ b/package.json @@ -21,10 +21,10 @@ "@emotion/styled": "11.11.5", "@graasp/chatbox": "3.1.0", "@graasp/map": "1.12.1", - "@graasp/query-client": "3.8.0", - "@graasp/sdk": "4.11.0", + "@graasp/query-client": "3.9.0", + "@graasp/sdk": "4.12.0", "@graasp/translations": "1.28.0", - "@graasp/ui": "4.18.2", + "@graasp/ui": "4.19.0", "@mui/icons-material": "5.15.18", "@mui/lab": "5.0.0-alpha.170", "@mui/material": "5.15.18", diff --git a/src/components/App.tsx b/src/components/App.tsx index f86d1e80e..b2542aa5c 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,5 +1,4 @@ -import { Outlet, useLocation } from 'react-router'; -import { Route, Routes } from 'react-router-dom'; +import { Outlet, Route, Routes, useLocation } from 'react-router-dom'; import { buildSignInPath, saveUrlForRedirection } from '@graasp/sdk'; import { CustomInitialLoader, withAuthorization } from '@graasp/ui'; diff --git a/src/components/context/FilterItemsContext.tsx b/src/components/context/FilterItemsContext.tsx index 06b13c971..c2ec117c6 100644 --- a/src/components/context/FilterItemsContext.tsx +++ b/src/components/context/FilterItemsContext.tsx @@ -1,5 +1,5 @@ import { createContext, useContext, useEffect, useMemo, useState } from 'react'; -import { useLocation } from 'react-router'; +import { useLocation } from 'react-router-dom'; import { DiscriminatedItem } from '@graasp/sdk'; diff --git a/src/components/item/ItemContent.tsx b/src/components/item/ItemContent.tsx index cc4d4bbd6..7f7a44bb6 100644 --- a/src/components/item/ItemContent.tsx +++ b/src/components/item/ItemContent.tsx @@ -1,4 +1,4 @@ -import { useOutletContext } from 'react-router'; +import { useOutletContext } from 'react-router-dom'; import { Container, Skeleton, styled } from '@mui/material'; @@ -107,21 +107,19 @@ const LinkContent = ({ item: LinkItemType; member?: Member | null; }): JSX.Element => ( - - - + ); /** diff --git a/src/components/item/form/DisplayNameForm.tsx b/src/components/item/form/DisplayNameForm.tsx deleted file mode 100644 index 72f2e07af..000000000 --- a/src/components/item/form/DisplayNameForm.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { ChangeEvent } from 'react'; - -import ClearIcon from '@mui/icons-material/Clear'; -import InfoIcon from '@mui/icons-material/Info'; -import { IconButton, Stack, TextField, Tooltip } from '@mui/material'; - -import { useBuilderTranslation } from '../../../config/i18n'; -import { ITEM_FORM_DISPLAY_NAME_INPUT_ID } from '../../../config/selectors'; -import { BUILDER } from '../../../langs/constants'; -import type { EditModalContentPropType } from './EditModalWrapper'; - -export type DisplayNameFormProps = EditModalContentPropType; - -const DisplayNameForm = ({ - updatedProperties, - setChanges, -}: DisplayNameFormProps): JSX.Element => { - const { t: translateBuilder } = useBuilderTranslation(); - - const handleDisplayNameInput = (event: ChangeEvent<{ value: string }>) => { - setChanges({ displayName: event.target.value }); - }; - - const handleClearClick = () => { - setChanges({ displayName: '' }); - }; - - return ( - - {translateBuilder(BUILDER.CREATE_NEW_ITEM_DISPLAY_NAME_LABEL)} - - - - - - - } - value={updatedProperties?.displayName} - onChange={handleDisplayNameInput} - // always shrink because setting name from defined app does not shrink automatically - InputLabelProps={{ shrink: true }} - // add a clear icon button - InputProps={{ - endAdornment: ( - - - - ), - }} - // only take full width when on small screen size - fullWidth - sx={{ my: 1 }} - /> - ); -}; - -export default DisplayNameForm; diff --git a/src/components/item/form/DocumentForm.tsx b/src/components/item/form/DocumentForm.tsx index 7c86682ed..fb1d92fc0 100644 --- a/src/components/item/form/DocumentForm.tsx +++ b/src/components/item/form/DocumentForm.tsx @@ -36,7 +36,6 @@ import { ITEM_FORM_DOCUMENT_TEXT_ID, } from '../../../config/selectors'; import { BUILDER } from '../../../langs/constants'; -import DisplayNameForm from './DisplayNameForm'; import type { EditModalContentPropType } from './EditModalWrapper'; import NameForm from './NameForm'; @@ -305,11 +304,6 @@ const DocumentForm = ({ required updatedProperties={updatedProperties} /> - { height={THUMBNAIL_DIMENSION} width={THUMBNAIL_DIMENSION} borderRadius={2} - bgcolor="#E4DFFF" + bgcolor={DEFAULT_LIGHT_PRIMARY_COLOR} alignItems="center" justifyContent="center" overflow="hidden" diff --git a/src/components/item/form/LinkForm.tsx b/src/components/item/form/LinkForm.tsx deleted file mode 100644 index 87ede1fb4..000000000 --- a/src/components/item/form/LinkForm.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { TextField, TextFieldProps } from '@mui/material'; - -import { LinkItemType, buildLinkExtra, getLinkExtra } from '@graasp/sdk'; - -import { useBuilderTranslation } from '../../../config/i18n'; -import { ITEM_FORM_LINK_INPUT_ID } from '../../../config/selectors'; -import { BUILDER } from '../../../langs/constants'; -import { isUrlValid } from '../../../utils/item'; - -type Props = { - onChange: (item: Partial) => void; - item?: LinkItemType; -}; - -const LinkForm = ({ onChange, item }: Props): JSX.Element => { - const { t: translateBuilder } = useBuilderTranslation(); - - const handleLinkInput: TextFieldProps['onChange'] = (event) => { - const newValue = event.target.value; - const hasProtocol = /^https?:\/\//; - onChange({ - ...item, - name: translateBuilder(BUILDER.LINK_DEFAULT_NAME), // todo: this is replaced by iframely - extra: buildLinkExtra({ - // when used inside the NewItem Modal this component does not receive the item prop - // so the https will not show, but it will be added when we submit the url. - url: hasProtocol.test(newValue) ? newValue : `https://${newValue}`, - html: '', - thumbnails: [], - icons: [], - }), - }); - }; - - let url = null; - if (item?.extra) { - ({ url } = getLinkExtra(item?.extra) || {}); - } - const isLinkInvalid = Boolean(url?.length) && !isUrlValid(url); - - return ( - - ); -}; - -export default LinkForm; diff --git a/src/components/item/form/link/LinkDescriptionField.tsx b/src/components/item/form/link/LinkDescriptionField.tsx new file mode 100644 index 000000000..75c36d8b1 --- /dev/null +++ b/src/components/item/form/link/LinkDescriptionField.tsx @@ -0,0 +1,60 @@ +import { ChangeEvent } from 'react'; + +import { IconButton, TextField } from '@mui/material'; + +import { Undo2Icon, XIcon } from 'lucide-react'; + +import { useBuilderTranslation } from '@/config/i18n'; +import { BUILDER } from '@/langs/constants'; + +type LinkDescriptionFieldProps = { + value: string | null | undefined; + onChange: (newValue: string) => void; + onRestore: () => void; + onClear: () => void; + showRestore: boolean; +}; +const LinkDescriptionField = ({ + value, + onChange, + onRestore, + onClear, + showRestore, +}: LinkDescriptionFieldProps): JSX.Element => { + const { t } = useBuilderTranslation(); + return ( + ) => onChange(newValue)} + InputProps={{ + endAdornment: ( + <> + + + + + + + + + ), + }} + /> + ); +}; +export default LinkDescriptionField; diff --git a/src/components/item/form/link/LinkForm.tsx b/src/components/item/form/link/LinkForm.tsx new file mode 100644 index 000000000..51834171c --- /dev/null +++ b/src/components/item/form/link/LinkForm.tsx @@ -0,0 +1,279 @@ +import { ChangeEvent, useEffect, useMemo, useState } from 'react'; + +import { + FormControl, + FormControlLabel, + FormLabel, + Link, + Radio, + RadioGroup, + Stack, + Typography, + styled, +} from '@mui/material'; + +import { + DiscriminatedItem, + ItemType, + LinkItemType, + buildLinkExtra, + getLinkExtra, +} from '@graasp/sdk'; +import { LinkCard, LinkItem } from '@graasp/ui'; + +import { hooks } from '@/config/queryClient'; + +import { useBuilderTranslation } from '../../../../config/i18n'; +import { BUILDER } from '../../../../langs/constants'; +import { isUrlValid } from '../../../../utils/item'; +import NameForm from '../NameForm'; +import LinkDescriptionField from './LinkDescriptionField'; +import LinkUrlField from './LinkUrlField'; +import { + LinkType, + getLinkType, + getSettingsFromLinkType, + normalizeURL, +} from './linkUtils'; + +type Props = { + onChange: (item: Partial) => void; + item?: LinkItemType; + updatedProperties: Partial; +}; + +const StyledFormControlLabel = styled(FormControlLabel)(({ theme }) => ({ + // remove weird default margins on label + margin: 0, + padding: theme.spacing(2), + borderRadius: theme.spacing(2), + // this allows to apply a style to the current element if if contains an input element that is checked + '&:has(input:checked)': { + // here we added a bit of opacity to the color used on the background of the icons cards + backgroundColor: '#E4DFFFB3', + }, +})); + +const StyledDiv = styled('div')(() => ({ + '& > div': { + height: '200px !important', + paddingBottom: '0 !important', + }, +})); + +const LinkForm = ({ + onChange, + item, + updatedProperties, +}: Props): JSX.Element => { + const [linkContent, setLinkContent] = useState(''); + const [isDescriptionDirty, setIsDescriptionDirty] = useState(false); + const { t: translateBuilder } = useBuilderTranslation(); + const { data: linkData } = hooks.useLinkMetadata(normalizeURL(linkContent)); + + // get value from the updatedProperties + const linkType = getLinkType(updatedProperties.settings); + + const handleLinkInput = (value: string) => { + setLinkContent(value); + onChange({ + extra: buildLinkExtra({ + // when used inside the NewItem Modal this component does not receive the item prop + // so the https will not show, but it will be added when we submit the url. + url: normalizeURL(value), + html: '', + thumbnails: [], + icons: [], + }), + }); + }; + + let url = ''; + let description: string | undefined = ''; + const extraProps = updatedProperties.extra; + if (extraProps && ItemType.LINK in extraProps) { + ({ url, description } = getLinkExtra(extraProps) || {}); + } + // link is considered valid if it is either empty, or it is a valid url + const isLinkValid = linkContent.length === 0 || isUrlValid(url); + + const onChangeLinkType = ({ + target: { value }, + }: ChangeEvent) => { + const settings = getSettingsFromLinkType(value); + onChange({ settings }); + }; + + const onClickClearURL = () => { + setLinkContent(''); + }; + const onClickClearDescription = () => { + onChange({ description: '' }); + setIsDescriptionDirty(false); + }; + const onClickRestoreDefaultDescription = () => { + onChange({ description: linkData?.description }); + setIsDescriptionDirty(false); + }; + const onChangeDescription = (value: string) => { + setIsDescriptionDirty(true); + onChange({ description: value }); + }; + + // apply the description from the api to the field + // this is only run once. + useEffect( + () => { + // this is the object on which we will define the props to be updated + const updatedProps: Partial = {}; + + if (!isDescriptionDirty && linkData?.description) { + updatedProps.description = linkData?.description; + } + if (linkData?.title) { + updatedProps.name = linkData.title; + } + if (linkData?.description) { + updatedProps.extra = buildLinkExtra({ + ...updatedProperties.extra?.embeddedLink, + url: updatedProperties.extra?.embeddedLink.url || '', + description: linkData.description, + }); + } + // update props in one call to remove issue of race updates + if (Object.keys(updatedProps).length) { + // this should be called only once ! + onChange(updatedProps); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [linkData], + ); + + const embeddedLinkPreview = useMemo( + () => ( + + ), + [url], + ); + + return ( + + + + + + + + {translateBuilder(BUILDER.CREATE_ITEM_LINK_TYPE_TITLE)} + + {linkContent ? ( + + {linkContent}} + control={} + /> + + } + control={} + slotProps={{ typography: { width: '100%', minWidth: '0px' } }} + sx={{ minWidth: '0px', width: '100%' }} + /> + {linkData?.html && linkData.html !== '' && ( + + } + control={} + slotProps={{ + typography: { + width: '100%', + minWidth: '0px', + }, + }} + /> + )} + { + // only show this options when embedding is allowed and there is no html code + // as the html will take precedence over showing the site as an iframe + // and some sites like daily motion actually allow both, we want to allow show the html setting + linkData?.isEmbeddingAllowed && linkData?.html === '' && ( + } + slotProps={{ + typography: { + width: '100%', + minWidth: '0px', + }, + }} + sx={{ + // this ensure the iframe takes up all horizontal space + '& iframe': { + width: '100%', + }, + }} + /> + ) + } + + ) : ( + + {translateBuilder(BUILDER.CREATE_ITEM_LINK_TYPE_HELPER_TEXT)} + + )} + + + + ); +}; + +export default LinkForm; diff --git a/src/components/item/form/link/LinkUrlField.tsx b/src/components/item/form/link/LinkUrlField.tsx new file mode 100644 index 000000000..5b312b9cc --- /dev/null +++ b/src/components/item/form/link/LinkUrlField.tsx @@ -0,0 +1,55 @@ +import { ChangeEvent } from 'react'; + +import { IconButton, TextField } from '@mui/material'; + +import { XIcon } from 'lucide-react'; + +import { useBuilderTranslation } from '@/config/i18n'; +import { ITEM_FORM_LINK_INPUT_ID } from '@/config/selectors'; +import { BUILDER } from '@/langs/constants'; + +type LinkUrlFieldProps = { + value: string; + isValid: boolean; + onClear: () => void; + onChange: (newValue: string) => void; +}; +const LinkUrlField = ({ + value, + isValid, + onClear, + onChange, +}: LinkUrlFieldProps): JSX.Element => { + const { t } = useBuilderTranslation(); + return ( + ) => onChange(newValue)} + helperText={isValid ? '' : t(BUILDER.CREATE_ITEM_LINK_INVALID_LINK_ERROR)} + InputLabelProps={{ shrink: true }} + InputProps={{ + endAdornment: ( + + + + ), + }} + fullWidth + required + /> + ); +}; +export default LinkUrlField; diff --git a/src/components/item/form/link/linkUtils.test.ts b/src/components/item/form/link/linkUtils.test.ts new file mode 100644 index 000000000..31dfb709a --- /dev/null +++ b/src/components/item/form/link/linkUtils.test.ts @@ -0,0 +1,85 @@ +import { describe, expect, it } from 'vitest'; + +import { + LinkType, + getLinkType, + getSettingsFromLinkType, + normalizeURL, +} from './linkUtils'; + +describe('Link Utils', () => { + describe('Normalize URL', () => { + it('Return correct HTTP url', () => { + const url = 'http://graasp.org'; + const newUrl = normalizeURL(url); + expect(newUrl).toEqual(url); + }); + it('Return correct HTTPS url', () => { + const url = 'https://graasp.org'; + const newUrl = normalizeURL(url); + expect(newUrl).toEqual(url); + }); + it('Adds protocol to url without', () => { + const url = 'graasp.org'; + const newUrl = normalizeURL(url); + expect(newUrl).toEqual(`https://${url}`); + }); + it('Adds protocol to url with path', () => { + const url = 'graasp.org/terms'; + const newUrl = normalizeURL(url); + expect(newUrl).toEqual(`https://${url}`); + }); + }); + + describe('Get Link Type', () => { + it('Default link type', () => { + const settings = {}; + const linkType = getLinkType(settings); + expect(linkType).toEqual(LinkType.Default); + }); + it('Default link type', () => { + const settings = { showLinkButton: false, showLinkIframe: false }; + const linkType = getLinkType(settings); + expect(linkType).toEqual(LinkType.Default); + }); + it('Fancy link type', () => { + const settings = { showLinkButton: true, showLinkIframe: false }; + const linkType = getLinkType(settings); + expect(linkType).toEqual(LinkType.Fancy); + }); + it('Embedded link type', () => { + const settings = { showLinkButton: false, showLinkIframe: true }; + const linkType = getLinkType(settings); + expect(linkType).toEqual(LinkType.Embedded); + }); + it('Prefer Embedded link type when both are true', () => { + const settings = { showLinkButton: true, showLinkIframe: true }; + const linkType = getLinkType(settings); + expect(linkType).toEqual(LinkType.Embedded); + }); + }); + + describe('Get settings from link type', () => { + it('Default type', () => { + const settings = getSettingsFromLinkType(LinkType.Default); + expect(settings).toEqual({ + showLinkIframe: false, + showLinkButton: false, + }); + }); + it('Fancy type', () => { + const settings = getSettingsFromLinkType(LinkType.Fancy); + expect(settings).toEqual({ + showLinkIframe: false, + showLinkButton: true, + }); + }); + it('Embedded type', () => { + const settings = getSettingsFromLinkType(LinkType.Embedded); + expect(settings).toEqual({ + showLinkIframe: true, + showLinkButton: false, + }); + }); + }); +}); diff --git a/src/components/item/form/link/linkUtils.ts b/src/components/item/form/link/linkUtils.ts new file mode 100644 index 000000000..9dc81351b --- /dev/null +++ b/src/components/item/form/link/linkUtils.ts @@ -0,0 +1,79 @@ +import { ItemSettings, LinkItemSettings, UnionOfConst } from '@graasp/sdk'; + +/** + * Enum representing the different ways a link can be shown + * `Embedded` will use an iframe to display the website content. + * If the link is to a rich media (video, etc), the embedded code will be used. + * `Fancy` will display a nice card similar to social media previews. + * `Default` will show the link as hyperlink text. + */ +export const LinkType = { + Embedded: 'embedded', + Fancy: 'fancy', + Default: 'default', +} as const; + +/** + * Add the protocol on the url if not set + * This function allows to enter urls without the protocol (http / https) + * and will add it to make url compliant to the spec. Defaults to adding HTTPS. + * @param url input url to be normalized + * @returns A string representing a url with the required protocol attached + */ +export const normalizeURL = (url: string): string => { + const hasProtocol = /^https?:\/\//; + + if (hasProtocol.test(url)) { + return url; + } + return `https://${url}`; +}; + +/** + * Find LinkType based on settings + * @param settings item settings from which to take the showIframe and shoWButton settings + * @returns the linkType to be used + */ +export const getLinkType = ( + settings?: LinkItemSettings & ItemSettings, +): UnionOfConst => { + if (settings?.showLinkIframe) { + return LinkType.Embedded; + } + if (settings?.showLinkButton) { + return LinkType.Fancy; + } + return LinkType.Default; +}; + +/** + * Convert a link type to settings to be saved in the item + * @param linkType The type of the link selected + * @returns the settings needed to represent that link type + */ +export const getSettingsFromLinkType = ( + linkType: string, +): { showLinkButton: boolean; showLinkIframe: boolean } => { + switch (linkType) { + case LinkType.Fancy: { + return { + showLinkIframe: false, + showLinkButton: true, + }; + } + case LinkType.Embedded: { + return { + showLinkIframe: true, + showLinkButton: false, + }; + } + // eslint-disable-next-line no-fallthrough + case LinkType.Default: + default: { + return { + showLinkIframe: false, + showLinkButton: false, + }; + } + } +}; diff --git a/src/components/item/publish/CategorySelection.tsx b/src/components/item/publish/CategorySelection.tsx index e7d9edd4d..662f10891 100644 --- a/src/components/item/publish/CategorySelection.tsx +++ b/src/components/item/publish/CategorySelection.tsx @@ -1,5 +1,5 @@ import { SyntheticEvent } from 'react'; -import { useParams } from 'react-router'; +import { useParams } from 'react-router-dom'; import { AutocompleteChangeReason, Box, Typography } from '@mui/material'; diff --git a/src/components/item/publish/ItemPublishTab.tsx b/src/components/item/publish/ItemPublishTab.tsx index 006228e9a..5ab000cec 100644 --- a/src/components/item/publish/ItemPublishTab.tsx +++ b/src/components/item/publish/ItemPublishTab.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { useOutletContext } from 'react-router'; +import { useOutletContext } from 'react-router-dom'; import { Cancel, diff --git a/src/components/item/settings/ItemLicenseSettings.tsx b/src/components/item/settings/ItemLicenseSettings.tsx index 77b61b8fb..24bb78d40 100644 --- a/src/components/item/settings/ItemLicenseSettings.tsx +++ b/src/components/item/settings/ItemLicenseSettings.tsx @@ -43,7 +43,7 @@ const ItemLicenseSettings = (): JSX.Element => { } additionalInfo={ - item.settings?.ccLicenseAdaption && ( + item.settings?.ccLicenseAdaption ? ( { allowCommercialUse={allowCommercialUse} iconSize={30} /> - ) + ) : undefined } /> - + ); diff --git a/src/components/main/ItemTypeTabs.tsx b/src/components/main/ItemTypeTabs.tsx index 005f2514f..557431491 100644 --- a/src/components/main/ItemTypeTabs.tsx +++ b/src/components/main/ItemTypeTabs.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { Tab, Tabs, styled } from '@mui/material'; -import { ItemType, LocalFileItemExtra, MimeTypes } from '@graasp/sdk'; +import { ItemType, MimeTypes } from '@graasp/sdk'; import { ItemIcon } from '@graasp/ui'; import { useBuilderTranslation } from '../../config/i18n'; @@ -52,8 +52,7 @@ const ItemTypeTabs = ({ onTypeChange, initialValue }: Props): JSX.Element => { ); @@ -73,7 +72,6 @@ const ItemTypeTabs = ({ onTypeChange, initialValue }: Props): JSX.Element => { } /> @@ -85,7 +83,6 @@ const ItemTypeTabs = ({ onTypeChange, initialValue }: Props): JSX.Element => { } /> @@ -97,7 +94,6 @@ const ItemTypeTabs = ({ onTypeChange, initialValue }: Props): JSX.Element => { } /> @@ -109,7 +105,6 @@ const ItemTypeTabs = ({ onTypeChange, initialValue }: Props): JSX.Element => { } /> @@ -121,7 +116,6 @@ const ItemTypeTabs = ({ onTypeChange, initialValue }: Props): JSX.Element => { } /> @@ -139,7 +133,6 @@ const ItemTypeTabs = ({ onTypeChange, initialValue }: Props): JSX.Element => { } /> @@ -151,7 +144,6 @@ const ItemTypeTabs = ({ onTypeChange, initialValue }: Props): JSX.Element => { } /> diff --git a/src/components/main/MainMenu.tsx b/src/components/main/MainMenu.tsx index 3cbd022c6..59fd71e45 100644 --- a/src/components/main/MainMenu.tsx +++ b/src/components/main/MainMenu.tsx @@ -1,4 +1,4 @@ -import { useLocation, useNavigate } from 'react-router'; +import { useLocation, useNavigate } from 'react-router-dom'; // import { BugReport } from '@mui/icons-material'; import { AutoStories, Bookmark, Delete, Folder } from '@mui/icons-material'; diff --git a/src/components/main/NewItemModal.tsx b/src/components/main/NewItemModal.tsx index c8483af98..21fee99b8 100644 --- a/src/components/main/NewItemModal.tsx +++ b/src/components/main/NewItemModal.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { useParams } from 'react-router'; +import { useParams } from 'react-router-dom'; import { Dialog, @@ -38,7 +38,7 @@ import AppForm from '../item/form/AppForm'; import DocumentForm from '../item/form/DocumentForm'; import useEtherpadForm from '../item/form/EtherpadForm'; import FolderForm from '../item/form/FolderForm'; -import LinkForm from '../item/form/LinkForm'; +import LinkForm from '../item/form/link/LinkForm'; import ImportH5P from './ImportH5P'; import ImportZip from './ImportZip'; import ItemTypeTabs from './ItemTypeTabs'; @@ -122,8 +122,7 @@ const NewItemModal = ({ // todo: notify user return false; } - // eslint-disable-next-line no-console - console.log(updatedPropertiesPerType[type]); + // todo: fix types return submitAndDisableConfirmButtonFor( () => @@ -230,7 +229,10 @@ const NewItemModal = ({ {translateBuilder(BUILDER.CREATE_ITEM_LINK_TITLE)} - + ); case ItemType.DOCUMENT: diff --git a/src/components/pages/item/ItemLoginWrapper.tsx b/src/components/pages/item/ItemLoginWrapper.tsx index 75e1f6076..47cd81f02 100644 --- a/src/components/pages/item/ItemLoginWrapper.tsx +++ b/src/components/pages/item/ItemLoginWrapper.tsx @@ -1,4 +1,4 @@ -import { useParams } from 'react-router'; +import { useParams } from 'react-router-dom'; import { ItemLoginAuthorization } from '@graasp/ui'; diff --git a/src/components/pages/item/ItemPageLayout.tsx b/src/components/pages/item/ItemPageLayout.tsx index 557bc8a36..cd237759b 100644 --- a/src/components/pages/item/ItemPageLayout.tsx +++ b/src/components/pages/item/ItemPageLayout.tsx @@ -1,6 +1,10 @@ import { useEffect } from 'react'; -import { Outlet, useOutletContext, useParams } from 'react-router'; -import { useSearchParams } from 'react-router-dom'; +import { + Outlet, + useOutletContext, + useParams, + useSearchParams, +} from 'react-router-dom'; import { Box } from '@mui/material'; diff --git a/src/components/pages/item/ItemScreen.tsx b/src/components/pages/item/ItemScreen.tsx index d9e91be80..bfc4c2da1 100644 --- a/src/components/pages/item/ItemScreen.tsx +++ b/src/components/pages/item/ItemScreen.tsx @@ -1,4 +1,4 @@ -import { useOutletContext } from 'react-router'; +import { useOutletContext } from 'react-router-dom'; import { ItemType } from '@graasp/sdk'; diff --git a/src/components/pages/item/ItemScreenLayout.tsx b/src/components/pages/item/ItemScreenLayout.tsx index 3da8353cf..849f5d264 100644 --- a/src/components/pages/item/ItemScreenLayout.tsx +++ b/src/components/pages/item/ItemScreenLayout.tsx @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { Outlet, useParams } from 'react-router'; +import { Outlet, useParams } from 'react-router-dom'; import { PermissionLevel, PermissionLevelCompare } from '@graasp/sdk'; import { Loader } from '@graasp/ui'; diff --git a/src/components/pages/item/ItemSettingsPage.tsx b/src/components/pages/item/ItemSettingsPage.tsx index a0a9d0d51..1e140c8b7 100644 --- a/src/components/pages/item/ItemSettingsPage.tsx +++ b/src/components/pages/item/ItemSettingsPage.tsx @@ -1,5 +1,9 @@ -import { Navigate, useOutletContext, useParams } from 'react-router'; -import { useSearchParams } from 'react-router-dom'; +import { + Navigate, + useOutletContext, + useParams, + useSearchParams, +} from 'react-router-dom'; import { PermissionLevel, PermissionLevelCompare } from '@graasp/sdk'; diff --git a/src/components/pages/item/LibrarySettingsPage.tsx b/src/components/pages/item/LibrarySettingsPage.tsx index b0d50df02..6798e2706 100644 --- a/src/components/pages/item/LibrarySettingsPage.tsx +++ b/src/components/pages/item/LibrarySettingsPage.tsx @@ -1,5 +1,9 @@ -import { Navigate, useOutletContext, useParams } from 'react-router'; -import { useSearchParams } from 'react-router-dom'; +import { + Navigate, + useOutletContext, + useParams, + useSearchParams, +} from 'react-router-dom'; import { PermissionLevel } from '@graasp/sdk'; diff --git a/src/langs/constants.ts b/src/langs/constants.ts index 2cdbc77a7..c3b883592 100644 --- a/src/langs/constants.ts +++ b/src/langs/constants.ts @@ -22,6 +22,10 @@ export const BUILDER = { CREATE_ITEM_LINK_INVALID_LINK_ERROR: 'CREATE_ITEM_LINK_INVALID_LINK_ERROR', CREATE_ITEM_LINK_LABEL: 'CREATE_ITEM_LINK_LABEL', CREATE_ITEM_LINK_TITLE: 'CREATE_ITEM_LINK_TITLE', + CREATE_ITEM_LINK_TYPE_TITLE: 'CREATE_ITEM_LINK_TYPE_TITLE', + CREATE_ITEM_LINK_TYPE_HELPER_TEXT: 'CREATE_ITEM_LINK_TYPE_HELPER_TEXT', + CREATE_ITEM_LINK_EMBED_TITLE: 'CREATE_ITEM_LINK_EMBED_TITLE', + CREATE_ITEM_LINK_EMBED_HELPER_TEXT: 'CREATE_ITEM_LINK_EMBED_HELPER_TEXT', CREATE_ITEM_NEW_FOLDER_TITLE: 'CREATE_ITEM_NEW_FOLDER_TITLE', CREATE_NEW_ITEM_APP_TITLE: 'CREATE_NEW_ITEM_APP_TITLE', CREATE_NEW_ITEM_APP_URL_LABEL: 'CREATE_NEW_ITEM_APP_URL_LABEL', @@ -35,6 +39,8 @@ export const BUILDER = { CREATE_NEW_ITEM_DISPLAY_NAME_LABEL: 'CREATE_NEW_ITEM_DISPLAY_NAME_LABEL', CREATE_NEW_ITEM_DISPLAY_NAME_HELPER_TEXT: 'CREATE_NEW_ITEM_DISPLAY_NAME_HELPER_TEXT', + CREATE_NEW_ITEM_DISPLAY_NAME_HELPER_TEXT_LINKFORM: + 'CREATE_NEW_ITEM_DISPLAY_NAME_HELPER_TEXT_LINKFORM', CREATE_SHORTCUT_BUTTON: 'CREATE_SHORTCUT_BUTTON', CREATE_SHORTCUT_DEFAULT_NAME: 'CREATE_SHORTCUT_DEFAULT_NAME', CREATE_SHORTCUT_MODAL_TITLE: 'CREATE_SHORTCUT_MODAL_TITLE', diff --git a/src/langs/en.json b/src/langs/en.json index bcba1b6b4..578e7d90a 100644 --- a/src/langs/en.json +++ b/src/langs/en.json @@ -23,6 +23,10 @@ "CREATE_ITEM_LINK_TITLE": "Add a Link", "CREATE_ITEM_NEW_FOLDER_TITLE": "Add a Folder", "CREATE_NEW_ITEM_APP_TITLE": "Add an Application", + "CREATE_ITEM_LINK_TYPE_TITLE": "Preview Type", + "CREATE_ITEM_LINK_TYPE_HELPER_TEXT": "To preview, please firstly insert your link above...", + "CREATE_ITEM_LINK_EMBED_TITLE": "Embed the website", + "CREATE_ITEM_LINK_EMBED_HELPER_TEXT": "Some websites may limit access when embedded.", "CREATE_NEW_ITEM_APP_URL_LABEL": "App URL", "CREATE_NEW_ITEM_DOCUMENT_TITLE": "Add a Text", "CREATE_NEW_ITEM_ETHERPAD_INFORMATIONS": "Etherpad is a collaborative real-time text editor", @@ -32,6 +36,7 @@ "CREATE_NEW_ITEM_NAME_HELPER_TEXT": "Show in Graasp builder", "CREATE_NEW_ITEM_DISPLAY_NAME_LABEL": "Display Name", "CREATE_NEW_ITEM_DISPLAY_NAME_HELPER_TEXT": "Show in Graasp player. Defaults to the 'Name' field. Customize it to your needs.", + "CREATE_NEW_ITEM_DISPLAY_NAME_HELPER_TEXT_LINKFORM": "Show in Graasp player. Defaults to the 'Link' field. Customize it to your needs.", "CREATE_SHORTCUT_BUTTON": "Create shortcut", "CREATE_SHORTCUT_BUTTON_zero": "Create shortcut", "CREATE_SHORTCUT_BUTTON_one": "Create shortcut to {{name}}", diff --git a/yarn.lock b/yarn.lock index e37356f46..72deefee2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -92,7 +92,7 @@ __metadata: languageName: node linkType: hard -"@ag-grid-community/styles@npm:31.3.2, @ag-grid-community/styles@npm:^31.3.1": +"@ag-grid-community/styles@npm:31.3.2": version: 31.3.2 resolution: "@ag-grid-community/styles@npm:31.3.2" checksum: 10/b651750803577419e6bbbc45b64d717d64c500332364f4523f84d9d384351f69a7d8eb83bd8343a4ec1530b0db0f26288e91e69516c6ade6e5f7dcf9a7e3078c @@ -106,6 +106,13 @@ __metadata: languageName: node linkType: hard +"@ag-grid-community/styles@npm:^31.3.1": + version: 31.3.1 + resolution: "@ag-grid-community/styles@npm:31.3.1" + checksum: 10/34c4e80f62891fe1d8881e233df38ace6ba03eb2934b3015bb8537937164dc2c261479d8d15d5c06e36f2bb84c3b27c7b65aaab773236190729bdfe32ec2c225 + languageName: node + linkType: hard + "@ampproject/remapping@npm:^2.2.0": version: 2.3.0 resolution: "@ampproject/remapping@npm:2.3.0" @@ -1574,26 +1581,26 @@ __metadata: languageName: node linkType: hard -"@graasp/query-client@npm:3.8.0": - version: 3.8.0 - resolution: "@graasp/query-client@npm:3.8.0" +"@graasp/query-client@npm:3.9.0": + version: 3.9.0 + resolution: "@graasp/query-client@npm:3.9.0" dependencies: "@tanstack/react-query": "npm:4.36.1" "@tanstack/react-query-devtools": "npm:4.36.1" - axios: "npm:1.6.8" + axios: "npm:1.7.2" http-status-codes: "npm:2.3.0" qs: "npm:6.12.1" peerDependencies: "@graasp/sdk": ^4.0.0 "@graasp/translations": ^1.23.0 react: ^18.0.0 - checksum: 10/8657f66530cfe5e28c28c54cb29cf392d41aae9365bf7f1dceb9ed1876211b34d7032a892d33c0317e6a90d48d4a203bd44a3cecb8f2337121f35dcc789fdbef + checksum: 10/23490dceb1be2f95759b296099165c2d20be0a7a5fa3b7176c034e2fd30242635d167af39e69074385704d4bc11c8b3aba694c9a5f11e954099b75c395da537b languageName: node linkType: hard -"@graasp/sdk@npm:4.11.0": - version: 4.11.0 - resolution: "@graasp/sdk@npm:4.11.0" +"@graasp/sdk@npm:4.12.0": + version: 4.12.0 + resolution: "@graasp/sdk@npm:4.12.0" dependencies: "@faker-js/faker": "npm:8.4.1" filesize: "npm:10.1.2" @@ -1602,7 +1609,7 @@ __metadata: peerDependencies: date-fns: ^3 uuid: ^9 - checksum: 10/629401ee513c8f74010aeb38e2efe1a36bfab2c8b4129e00fa8ab5ce10b4103de4e2b374de3aa4f63bb9bb4e13acf39a3dadb6f4dbf51439514e8ae17cc9ca95 + checksum: 10/10f37882a6d926d0fd1e8e26edaa312a6ef12d69df98e909f4841d7873a4e15a0b45eb04f3cb6e79eb112b6e37bc1e5993591e81a13ddf8eef96c6db57f6b826 languageName: node linkType: hard @@ -1677,19 +1684,19 @@ __metadata: languageName: node linkType: hard -"@graasp/ui@npm:4.18.2": - version: 4.18.2 - resolution: "@graasp/ui@npm:4.18.2" +"@graasp/ui@npm:4.19.0": + version: 4.19.0 + resolution: "@graasp/ui@npm:4.19.0" dependencies: "@ag-grid-community/client-side-row-model": "npm:31.3.1" "@ag-grid-community/react": "npm:^31.3.1" "@ag-grid-community/styles": "npm:^31.3.1" - "@storybook/react-vite": "npm:8.1.1" + "@storybook/react-vite": "npm:8.1.3" http-status-codes: "npm:2.3.0" interweave: "npm:13.1.0" katex: "npm:0.16.10" lodash.truncate: "npm:4.4.2" - lucide-react: "npm:0.378.0" + lucide-react: "npm:0.379.0" react-cookie-consent: "npm:9.0.0" react-quill: "npm:2.0.0" react-rnd: "npm:10.4.10" @@ -1712,7 +1719,7 @@ __metadata: react-router-dom: ^6.11.0 stylis: ^4.1.3 stylis-plugin-rtl: ^2.1.1 - checksum: 10/819c2d8bf63008d7b7cd983a2a2750d4b2259c870dfaa17e826880424ddcc729803908ee3b41661064e4102da9a79e4734cd2df4f402ca1a34592ad7b34b694c + checksum: 10/48dc536e60bc3ae1ce2285b7d6174f82c6726bc40473bbf43ac9d3b76e85ed64d23fa42e5340c947b0036fac6863b8d80e55ffbd24c17e722557474f2eaece49 languageName: node linkType: hard @@ -2546,19 +2553,19 @@ __metadata: languageName: node linkType: hard -"@storybook/builder-vite@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/builder-vite@npm:8.1.1" - dependencies: - "@storybook/channels": "npm:8.1.1" - "@storybook/client-logger": "npm:8.1.1" - "@storybook/core-common": "npm:8.1.1" - "@storybook/core-events": "npm:8.1.1" - "@storybook/csf-plugin": "npm:8.1.1" - "@storybook/node-logger": "npm:8.1.1" - "@storybook/preview": "npm:8.1.1" - "@storybook/preview-api": "npm:8.1.1" - "@storybook/types": "npm:8.1.1" +"@storybook/builder-vite@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/builder-vite@npm:8.1.3" + dependencies: + "@storybook/channels": "npm:8.1.3" + "@storybook/client-logger": "npm:8.1.3" + "@storybook/core-common": "npm:8.1.3" + "@storybook/core-events": "npm:8.1.3" + "@storybook/csf-plugin": "npm:8.1.3" + "@storybook/node-logger": "npm:8.1.3" + "@storybook/preview": "npm:8.1.3" + "@storybook/preview-api": "npm:8.1.3" + "@storybook/types": "npm:8.1.3" "@types/find-cache-dir": "npm:^3.2.1" browser-assert: "npm:^1.2.1" es-module-lexer: "npm:^1.5.0" @@ -2579,7 +2586,7 @@ __metadata: optional: true vite-plugin-glimmerx: optional: true - checksum: 10/e8e4d8fe49bca7da86d1f560919d13a3b1fe9649eef1e2ef75ecf7ac2a7bdaf9e83e5a85fee9fc8f39a9cb047b53fa1f77b959e16bce3c1b749a1cd77420429e + checksum: 10/1bb5ec6dcfcabc2a9f18e7591d22b4e351785758b2ee2deac06dec4b1f51a77c55948e6c26a9764601912f63eee9bb628d4f1d9e05e536d0e4b5246b51b22a61 languageName: node linkType: hard @@ -2597,16 +2604,16 @@ __metadata: languageName: node linkType: hard -"@storybook/channels@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/channels@npm:8.1.1" +"@storybook/channels@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/channels@npm:8.1.3" dependencies: - "@storybook/client-logger": "npm:8.1.1" - "@storybook/core-events": "npm:8.1.1" + "@storybook/client-logger": "npm:8.1.3" + "@storybook/core-events": "npm:8.1.3" "@storybook/global": "npm:^5.0.0" telejson: "npm:^7.2.0" tiny-invariant: "npm:^1.3.1" - checksum: 10/c60349f3ea1306d17eee43cca352b5d1dc1431ebdbce80181da3f45e49fb479090094f73c6f55a10763951917ad0bbae02558fc392712a4a098d1a79710080d6 + checksum: 10/c7e0c7765b355499aadfe8b74e9723a38d48e0f1d83dd54baa89c1cc6ec3ccc024c55bfd4d5f9d83d3b37c5e6639823da57f32096f96395d7150ed76cc420b95 languageName: node linkType: hard @@ -2619,12 +2626,12 @@ __metadata: languageName: node linkType: hard -"@storybook/client-logger@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/client-logger@npm:8.1.1" +"@storybook/client-logger@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/client-logger@npm:8.1.3" dependencies: "@storybook/global": "npm:^5.0.0" - checksum: 10/0255552c93c66e2d11fa282a76e813861427e7cb0c3dc860e8d686bfd4cbceb37f04589d3540db39eb1e37761761d964e1d0266cbe3074e1dcc80af26cd5f153 + checksum: 10/c7735d2ef39c07b89faade51fe26a56816eadc61e9b4ab952c087dbbd7e7ef3acfd49d437460d8503cb02d17613a4734267c5a56ccc6f9f2a1b2330b8f5459e5 languageName: node linkType: hard @@ -2669,14 +2676,14 @@ __metadata: languageName: node linkType: hard -"@storybook/core-common@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/core-common@npm:8.1.1" +"@storybook/core-common@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/core-common@npm:8.1.3" dependencies: - "@storybook/core-events": "npm:8.1.1" - "@storybook/csf-tools": "npm:8.1.1" - "@storybook/node-logger": "npm:8.1.1" - "@storybook/types": "npm:8.1.1" + "@storybook/core-events": "npm:8.1.3" + "@storybook/csf-tools": "npm:8.1.3" + "@storybook/node-logger": "npm:8.1.3" + "@storybook/types": "npm:8.1.3" "@yarnpkg/fslib": "npm:2.10.3" "@yarnpkg/libzip": "npm:2.3.0" chalk: "npm:^4.1.0" @@ -2707,7 +2714,7 @@ __metadata: peerDependenciesMeta: prettier: optional: true - checksum: 10/38d98943c4e5ced16d35a477132471d04fc6da0cef1ebe4c3d4e026213b02f5625fe1250c3c7da403486a007415e06e639eb50baca30eb4beb2a945715cf55f4 + checksum: 10/29cd5c88715b5967d59a06836dfc751a921d4d5075c9bcd99fc2f9972f77130e08a5f671224da1bc93433dc10813fbdeaa44294575ce8da4e45750bbf9ffbc11 languageName: node linkType: hard @@ -2720,13 +2727,13 @@ __metadata: languageName: node linkType: hard -"@storybook/core-events@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/core-events@npm:8.1.1" +"@storybook/core-events@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/core-events@npm:8.1.3" dependencies: "@storybook/csf": "npm:^0.1.7" ts-dedent: "npm:^2.0.0" - checksum: 10/46d88ff57ecb4a1ebb4698354bf52e5191b8ca676f41b467ccd8d56b63e55455aa38835f2d88b7ddcfc61f60e4ce45fab7453bc644562c0fd4b81887934d0e99 + checksum: 10/dc4cf5a7d0f36fff14ce7e9630e44f826ea748700753cd53be64a7d7b0d3e99b227224eb72267a92baf533823579bd51a9b6999926736e0067b437ca1d0452ed languageName: node linkType: hard @@ -2740,13 +2747,13 @@ __metadata: languageName: node linkType: hard -"@storybook/csf-plugin@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/csf-plugin@npm:8.1.1" +"@storybook/csf-plugin@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/csf-plugin@npm:8.1.3" dependencies: - "@storybook/csf-tools": "npm:8.1.1" + "@storybook/csf-tools": "npm:8.1.3" unplugin: "npm:^1.3.1" - checksum: 10/3b702f40561c2d856091ba739c9ddbe67ac0cb3fda580738581d1767ccfa33149fc0a7f60ed00b9088f699614d3afbcd7778762e2a89fcd6da9bea9781c83b42 + checksum: 10/d987a38225594e231aa5ef45355611ff9bd96a9f1b24cf2044f09e9100b3e6b895fb420d6a226b4e36fa3cf3baee82071fd04fbef01d53a9dab50e3dccac1025 languageName: node linkType: hard @@ -2767,20 +2774,20 @@ __metadata: languageName: node linkType: hard -"@storybook/csf-tools@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/csf-tools@npm:8.1.1" +"@storybook/csf-tools@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/csf-tools@npm:8.1.3" dependencies: "@babel/generator": "npm:^7.24.4" "@babel/parser": "npm:^7.24.4" "@babel/traverse": "npm:^7.24.1" "@babel/types": "npm:^7.24.0" "@storybook/csf": "npm:^0.1.7" - "@storybook/types": "npm:8.1.1" + "@storybook/types": "npm:8.1.3" fs-extra: "npm:^11.1.0" recast: "npm:^0.23.5" ts-dedent: "npm:^2.0.0" - checksum: 10/d3b2e86113ada8be829395f69aaedf6f2652512edace5dd71f4391695a2f5caa800903b63f9df21a698ce56c574b965332dad67e52ca6d4fbc3d6946dc842305 + checksum: 10/5d385ea82cc698e6a51600c6ed7826e00925e9c8d116aefe4fd7d269096099801eb02aed303de3316c2f957d3110f39214208be24ea6566151735b4a188abc0f languageName: node linkType: hard @@ -2817,19 +2824,19 @@ __metadata: languageName: node linkType: hard -"@storybook/docs-tools@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/docs-tools@npm:8.1.1" +"@storybook/docs-tools@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/docs-tools@npm:8.1.3" dependencies: - "@storybook/core-common": "npm:8.1.1" - "@storybook/core-events": "npm:8.1.1" - "@storybook/preview-api": "npm:8.1.1" - "@storybook/types": "npm:8.1.1" + "@storybook/core-common": "npm:8.1.3" + "@storybook/core-events": "npm:8.1.3" + "@storybook/preview-api": "npm:8.1.3" + "@storybook/types": "npm:8.1.3" "@types/doctrine": "npm:^0.0.3" assert: "npm:^2.1.0" doctrine: "npm:^3.0.0" lodash: "npm:^4.17.21" - checksum: 10/8be375dd105b52171ecd385b087f911fcf8f5d5684fa6d8c67d1d75764ec1b4fcb43104b5b2b472ded2a9149d8ac8626608af6ac37aae8011546c94c81faabec + checksum: 10/3eac6ddd48db53c9f8759cfcc9dd01958bb396ea194c9d4705a7a2016e6ff19996e71148cca4691ea64de5452ba5b64eba86d811dd38d805988ce1d63a216865 languageName: node linkType: hard @@ -2847,10 +2854,10 @@ __metadata: languageName: node linkType: hard -"@storybook/node-logger@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/node-logger@npm:8.1.1" - checksum: 10/5c471b1dbcb85412c20981740b68c3592749e06b6d65732b649a1e6b3d8c0f9aa6897e02a694e48436d11480f951cd5889248628695a097da9e2c0efcb78ec29 +"@storybook/node-logger@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/node-logger@npm:8.1.3" + checksum: 10/b3e372f7c56c5a8e60144ca8fd1755a964ceaf33031357649ea7da09a23756864df44f4ad081562ba81f034ac1861e4deb5bd129197e42e03f4a5d2037937cee languageName: node linkType: hard @@ -2876,16 +2883,16 @@ __metadata: languageName: node linkType: hard -"@storybook/preview-api@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/preview-api@npm:8.1.1" +"@storybook/preview-api@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/preview-api@npm:8.1.3" dependencies: - "@storybook/channels": "npm:8.1.1" - "@storybook/client-logger": "npm:8.1.1" - "@storybook/core-events": "npm:8.1.1" + "@storybook/channels": "npm:8.1.3" + "@storybook/client-logger": "npm:8.1.3" + "@storybook/core-events": "npm:8.1.3" "@storybook/csf": "npm:^0.1.7" "@storybook/global": "npm:^5.0.0" - "@storybook/types": "npm:8.1.1" + "@storybook/types": "npm:8.1.3" "@types/qs": "npm:^6.9.5" dequal: "npm:^2.0.2" lodash: "npm:^4.17.21" @@ -2894,7 +2901,7 @@ __metadata: tiny-invariant: "npm:^1.3.1" ts-dedent: "npm:^2.0.0" util-deprecate: "npm:^1.0.2" - checksum: 10/fb6c9e38d86f039075b80ec1b8b5eccd2ef624b4f393760de243a79b60e108e6afb25ce50a3b01eb749575f104ebeeae6829b76216273b1e81e226e3e0c08745 + checksum: 10/5b760236cfee5fe268f9d997cdbcb7bcb7119aea583c515a1990e08573b112c8c6c979959837ae89e988bfda9767cd2c7f7b408fe780ba92ca47e52e7e7f053f languageName: node linkType: hard @@ -2905,10 +2912,10 @@ __metadata: languageName: node linkType: hard -"@storybook/preview@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/preview@npm:8.1.1" - checksum: 10/220dc8a668ec83ac38f656f3557c5c95e6cd69330b84298ab2863896fe63b39c1023296d097d235a65563db01e73345f89323f85160f027e9f57a66da9cfec5b +"@storybook/preview@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/preview@npm:8.1.3" + checksum: 10/84abd5e08369bb0236cc66c9300edc38bf1ce370adeced6bb90da0ea8aa58c8c05f987c1612acd16a46577c5d8b4aec0b3856931288d225a5b43979100f77245 languageName: node linkType: hard @@ -2922,13 +2929,13 @@ __metadata: languageName: node linkType: hard -"@storybook/react-dom-shim@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/react-dom-shim@npm:8.1.1" +"@storybook/react-dom-shim@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/react-dom-shim@npm:8.1.3" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - checksum: 10/db207111ea90ed6a31d7280e56586c3c048d971f6b8ac8f6708af8e71de9cb462a0138ac7618b857f1d6f188ca7bbec1d081c964e1c31a498c330da5d1843e41 + checksum: 10/493df9b4d3f5241007c2d268e684002d499e5f6532538f40c4348fc5272b8f2732e2a94a4ccedc8f28c715e38f0acb55edcda6c540a855f344225e48159ca1e8 languageName: node linkType: hard @@ -2951,16 +2958,16 @@ __metadata: languageName: node linkType: hard -"@storybook/react-vite@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/react-vite@npm:8.1.1" +"@storybook/react-vite@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/react-vite@npm:8.1.3" dependencies: "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.3.1" "@rollup/pluginutils": "npm:^5.0.2" - "@storybook/builder-vite": "npm:8.1.1" - "@storybook/node-logger": "npm:8.1.1" - "@storybook/react": "npm:8.1.1" - "@storybook/types": "npm:8.1.1" + "@storybook/builder-vite": "npm:8.1.3" + "@storybook/node-logger": "npm:8.1.3" + "@storybook/react": "npm:8.1.3" + "@storybook/types": "npm:8.1.3" find-up: "npm:^5.0.0" magic-string: "npm:^0.30.0" react-docgen: "npm:^7.0.0" @@ -2970,7 +2977,7 @@ __metadata: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta vite: ^4.0.0 || ^5.0.0 - checksum: 10/5d518889cafd12861bab416c32580e334cd5737862331c03052db2e65772b935c7e1803936b62bb71d904584f47e0d78bca4d6b8ec617bbf2c931122b7640fde + checksum: 10/a102eab5dbad5e222892ebe6e4225500ddd36537f990f884e46b6b901335e50a954eb528527f7952bc0340eda904260ac84ffe48c91da1ce718a5be14b0aefbc languageName: node linkType: hard @@ -3010,16 +3017,16 @@ __metadata: languageName: node linkType: hard -"@storybook/react@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/react@npm:8.1.1" +"@storybook/react@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/react@npm:8.1.3" dependencies: - "@storybook/client-logger": "npm:8.1.1" - "@storybook/docs-tools": "npm:8.1.1" + "@storybook/client-logger": "npm:8.1.3" + "@storybook/docs-tools": "npm:8.1.3" "@storybook/global": "npm:^5.0.0" - "@storybook/preview-api": "npm:8.1.1" - "@storybook/react-dom-shim": "npm:8.1.1" - "@storybook/types": "npm:8.1.1" + "@storybook/preview-api": "npm:8.1.3" + "@storybook/react-dom-shim": "npm:8.1.3" + "@storybook/types": "npm:8.1.3" "@types/escodegen": "npm:^0.0.6" "@types/estree": "npm:^0.0.51" "@types/node": "npm:^18.0.0" @@ -3042,7 +3049,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/a2c50c12d3b284ea1bee13a93033f36968a0963c84dabb254225865faebacdbbe51e594b39abc8fe8f8d0f04a4a509fabdaa987a32614a18cb590b5f828c2fe1 + checksum: 10/8646b3afe337b2544e23a75ea8772578ac839e3d4de29bb401acc9792ea39c464c896db261bb776ad2e2bf141dd7fbdf893c975698872b9c756fc662dec1148a languageName: node linkType: hard @@ -3058,14 +3065,14 @@ __metadata: languageName: node linkType: hard -"@storybook/types@npm:8.1.1": - version: 8.1.1 - resolution: "@storybook/types@npm:8.1.1" +"@storybook/types@npm:8.1.3": + version: 8.1.3 + resolution: "@storybook/types@npm:8.1.3" dependencies: - "@storybook/channels": "npm:8.1.1" + "@storybook/channels": "npm:8.1.3" "@types/express": "npm:^4.7.0" file-system-cache: "npm:2.3.0" - checksum: 10/f367334074e3f4464bcd0efdad75a0110a11d72265f8dc398d2e53a62b9d77dc159006a0db50bd63f0844c3466d7c7a099a0a81cddb87acfeb2e3d178d6cbfbb + checksum: 10/856dc930023a758e57a1512a9cfabf321f382fbfd78bcb27466a53850f857ed3f43351ca1a3003fbcf194dfe6296488e0dc75c6bfa80b3884e56517bfc4c8119 languageName: node linkType: hard @@ -4875,6 +4882,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:1.7.2": + version: 1.7.2 + resolution: "axios@npm:1.7.2" + dependencies: + follow-redirects: "npm:^1.15.6" + form-data: "npm:^4.0.0" + proxy-from-env: "npm:^1.1.0" + checksum: 10/6ae80dda9736bb4762ce717f1a26ff997d94672d3a5799ad9941c24d4fb019c1dff45be8272f08d1975d7950bac281f3ba24aff5ecd49ef5a04d872ec428782f + languageName: node + linkType: hard + "axobject-query@npm:^3.2.1": version: 3.2.1 resolution: "axobject-query@npm:3.2.1" @@ -7901,10 +7919,10 @@ __metadata: "@emotion/styled": "npm:11.11.5" "@graasp/chatbox": "npm:3.1.0" "@graasp/map": "npm:1.12.1" - "@graasp/query-client": "npm:3.8.0" - "@graasp/sdk": "npm:4.11.0" + "@graasp/query-client": "npm:3.9.0" + "@graasp/sdk": "npm:4.12.0" "@graasp/translations": "npm:1.28.0" - "@graasp/ui": "npm:4.18.2" + "@graasp/ui": "npm:4.19.0" "@mui/icons-material": "npm:5.15.18" "@mui/lab": "npm:5.0.0-alpha.170" "@mui/material": "npm:5.15.18" @@ -9750,6 +9768,15 @@ __metadata: languageName: node linkType: hard +"lucide-react@npm:0.379.0": + version: 0.379.0 + resolution: "lucide-react@npm:0.379.0" + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 + checksum: 10/e2231158df68e08c1a4adbbd14c92380f6b4b541ce0dcacef1a05a17d8bc7e7329bb9f3f34ed0800afda82ef5c3f972749ab4d2f56a536f25ee42d81d8abcbc6 + languageName: node + linkType: hard + "lz-string@npm:^1.5.0": version: 1.5.0 resolution: "lz-string@npm:1.5.0"