mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-25 15:47:55 +01:00
WIP: Share dialog with mock support
This commit is contained in:
parent
c440e2426c
commit
2ae76dc08d
@ -22,6 +22,8 @@ export type Label = {
|
|||||||
iconName: string;
|
iconName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Role = 'owner' | 'editor' | 'viewer';
|
||||||
|
|
||||||
export type MapInfo = {
|
export type MapInfo = {
|
||||||
id: number;
|
id: number;
|
||||||
starred: boolean;
|
starred: boolean;
|
||||||
@ -33,7 +35,7 @@ export type MapInfo = {
|
|||||||
lastModificationTime: string;
|
lastModificationTime: string;
|
||||||
description: string;
|
description: string;
|
||||||
isPublic: boolean;
|
isPublic: boolean;
|
||||||
role: 'owner' | 'editor' | 'viewer'
|
role: Role;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChangeHistory = {
|
export type ChangeHistory = {
|
||||||
@ -64,6 +66,12 @@ export type AccountInfo = {
|
|||||||
locale: Locale;
|
locale: Locale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Permission = {
|
||||||
|
name?: string;
|
||||||
|
email: string;
|
||||||
|
role: Role;
|
||||||
|
}
|
||||||
|
|
||||||
interface Client {
|
interface Client {
|
||||||
deleteAccount(): Promise<void>
|
deleteAccount(): Promise<void>
|
||||||
importMap(model: ImportMapInfo): Promise<number>
|
importMap(model: ImportMapInfo): Promise<number>
|
||||||
@ -72,11 +80,14 @@ interface Client {
|
|||||||
deleteMap(id: number): Promise<void>;
|
deleteMap(id: number): Promise<void>;
|
||||||
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
|
renameMap(id: number, basicInfo: BasicMapInfo): Promise<void>;
|
||||||
fetchAllMaps(): Promise<MapInfo[]>;
|
fetchAllMaps(): Promise<MapInfo[]>;
|
||||||
|
fetchMapPermissions(id: number): Promise<Permission[]>;
|
||||||
|
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void>;
|
||||||
|
|
||||||
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number>;
|
duplicateMap(id: number, basicInfo: BasicMapInfo): Promise<number>;
|
||||||
|
|
||||||
updateAccountLanguage(locale: LocaleCode): Promise<void>;
|
updateAccountLanguage(locale: LocaleCode): Promise<void>;
|
||||||
updateAccountPassword(pasword: string): Promise<void>;
|
updateAccountPassword(pasword: string): Promise<void>;
|
||||||
updateAccountInfo(firstname: string,lastname: string): Promise<void>;
|
updateAccountInfo(firstname: string, lastname: string): Promise<void>;
|
||||||
|
|
||||||
updateStarred(id: number, starred: boolean): Promise<void>;
|
updateStarred(id: number, starred: boolean): Promise<void>;
|
||||||
updateMapToPublic(id: number, starred: boolean): Promise<void>;
|
updateMapToPublic(id: number, starred: boolean): Promise<void>;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import Client, { AccountInfo, BasicMapInfo, ChangeHistory, ImportMapInfo, Label, MapInfo, NewUser } from '..';
|
import Client, { AccountInfo, BasicMapInfo, ChangeHistory, ImportMapInfo, Label, MapInfo, NewUser, Permission } from '..';
|
||||||
import { LocaleCode, localeFromStr } from '../../app-i18n';
|
import { LocaleCode, localeFromStr } from '../../app-i18n';
|
||||||
|
|
||||||
class MockClient implements Client {
|
class MockClient implements Client {
|
||||||
private maps: MapInfo[] = [];
|
private maps: MapInfo[] = [];
|
||||||
private labels: Label[] = [];
|
private labels: Label[] = [];
|
||||||
|
private permissionsByMap: Map<number, Permission[]> = new Map();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
||||||
@ -22,6 +24,7 @@ class MockClient implements Client {
|
|||||||
): MapInfo {
|
): MapInfo {
|
||||||
return { id, title, labels, createdBy: creator, creationTime, lastModificationBy: modifiedByUser, lastModificationTime: modifiedTime, starred, description, isPublic, role };
|
return { id, title, labels, createdBy: creator, creationTime, lastModificationBy: modifiedByUser, lastModificationTime: modifiedTime, starred, description, isPublic, role };
|
||||||
}
|
}
|
||||||
|
|
||||||
this.maps = [
|
this.maps = [
|
||||||
createMapInfo(1, true, "El Mapa", [], "Paulo", "2008-06-02T00:00:00Z", "Berna", "2008-06-02T00:00:00Z", "", true, 'owner'),
|
createMapInfo(1, true, "El Mapa", [], "Paulo", "2008-06-02T00:00:00Z", "Berna", "2008-06-02T00:00:00Z", "", true, 'owner'),
|
||||||
createMapInfo(11, false, "El Mapa3", [1, 2, 3], "Paulo3", "2008-06-02T00:00:00Z", "Berna", "2008-06-02T00:00:00Z", "", false, 'editor'),
|
createMapInfo(11, false, "El Mapa3", [1, 2, 3], "Paulo3", "2008-06-02T00:00:00Z", "Berna", "2008-06-02T00:00:00Z", "", false, 'editor'),
|
||||||
@ -32,7 +35,28 @@ class MockClient implements Client {
|
|||||||
{ id: 1, title: "Red Label", iconName: "", color: 'red' },
|
{ id: 1, title: "Red Label", iconName: "", color: 'red' },
|
||||||
{ id: 2, title: "Blue Label", iconName: "", color: 'blue' }
|
{ id: 2, title: "Blue Label", iconName: "", color: 'blue' }
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void> {
|
||||||
|
let perm = this.permissionsByMap.get(id) || [];
|
||||||
|
perm = perm.concat(permissions);
|
||||||
|
this.permissionsByMap.set(id, perm);
|
||||||
|
|
||||||
|
console.log(`Message ${message}`)
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchMapPermissions(id: number): Promise<Permission[]> {
|
||||||
|
let perm = this.permissionsByMap.get(id);
|
||||||
|
if (!perm) {
|
||||||
|
perm = [{
|
||||||
|
name: 'Cosme Sharing',
|
||||||
|
email: 'pepe@gmail.com',
|
||||||
|
role: 'editor'
|
||||||
|
}];
|
||||||
|
this.permissionsByMap.set(id, perm);
|
||||||
|
}
|
||||||
|
return Promise.resolve(perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAccount(): Promise<void> {
|
deleteAccount(): Promise<void> {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Client, { ErrorInfo, MapInfo, BasicMapInfo, NewUser, Label, ChangeHistory, AccountInfo, ImportMapInfo } from '..';
|
import Client, { ErrorInfo, MapInfo, BasicMapInfo, NewUser, Label, ChangeHistory, AccountInfo, ImportMapInfo, Permission } from '..';
|
||||||
import { LocaleCode, localeFromStr, Locales } from '../../app-i18n';
|
import { LocaleCode, localeFromStr, Locales } from '../../app-i18n';
|
||||||
|
|
||||||
export default class RestClient implements Client {
|
export default class RestClient implements Client {
|
||||||
@ -10,6 +10,15 @@ export default class RestClient implements Client {
|
|||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
this.sessionExpired = sessionExpired;
|
this.sessionExpired = sessionExpired;
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
addMapPermissions(id: number, message: string, permissions: Permission[]): Promise<void> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchMapPermissions(id: number): Promise<Permission[]> {
|
||||||
|
throw new Error('Method not implemented.' + id);
|
||||||
|
}
|
||||||
|
|
||||||
deleteAccount(): Promise<void> {
|
deleteAccount(): Promise<void> {
|
||||||
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
const handler = (success: () => void, reject: (error: ErrorInfo) => void) => {
|
||||||
axios.delete(this.baseUrl + `/c/restful/account`,
|
axios.delete(this.baseUrl + `/c/restful/account`,
|
||||||
|
@ -24,7 +24,7 @@ const ConfigStatusMessage = ({ enabled = false }: ConfigStatusProps): React.Reac
|
|||||||
</p>
|
</p>
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
return result ? result : null;
|
return result || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LoginError = () => {
|
const LoginError = () => {
|
||||||
|
@ -32,7 +32,7 @@ const AccountMenu = (): React.ReactElement => {
|
|||||||
const account = fetchAccount();
|
const account = fetchAccount();
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip title={`${account?.firstname} ${account?.lastname} <${account?.email}>`}>
|
<Tooltip arrow={true} title={`${account?.firstname} ${account?.lastname} <${account?.email}>`}>
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleMenu}>
|
onClick={handleMenu}>
|
||||||
<AccountCircle fontSize="large" style={{ color: 'black' }} />
|
<AccountCircle fontSize="large" style={{ color: 'black' }} />
|
||||||
|
@ -5,6 +5,7 @@ import { StyledDialog, StyledDialogActions, StyledDialogContent, StyledDialogTit
|
|||||||
import GlobalError from "../../../form/global-error";
|
import GlobalError from "../../../form/global-error";
|
||||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
|
import { PaperProps } from "@material-ui/core/Paper";
|
||||||
|
|
||||||
export type DialogProps = {
|
export type DialogProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -18,10 +19,11 @@ export type DialogProps = {
|
|||||||
submitButton?: string;
|
submitButton?: string;
|
||||||
actionUrl?: string;
|
actionUrl?: string;
|
||||||
maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
|
maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
|
||||||
|
PaperProps?: Partial<PaperProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BaseDialog = (props: DialogProps): React.ReactElement => {
|
const BaseDialog = (props: DialogProps): React.ReactElement => {
|
||||||
const { onClose, onSubmit, maxWidth = 'sm' } = props;
|
const { onClose, onSubmit, maxWidth = 'sm', PaperProps } = props;
|
||||||
|
|
||||||
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -31,13 +33,13 @@ const BaseDialog = (props: DialogProps): React.ReactElement => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const description = props.description ? (<DialogContentText>{props.description}</DialogContentText>) : null;
|
const description = props.description ? (<DialogContentText>{props.description}</DialogContentText>) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<StyledDialog
|
<StyledDialog
|
||||||
open={true}
|
open={true}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
maxWidth={maxWidth}
|
maxWidth={maxWidth}
|
||||||
|
PaperProps={PaperProps}
|
||||||
fullWidth={true}>
|
fullWidth={true}>
|
||||||
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
<form autoComplete="off" onSubmit={handleOnSubmit}>
|
||||||
<StyledDialogTitle>
|
<StyledDialogTitle>
|
||||||
|
@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { useMutation, useQueryClient } from "react-query";
|
import { useMutation, useQueryClient } from "react-query";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import Client from "../../../../classes/client";
|
import Client, { ErrorInfo } from "../../../../classes/client";
|
||||||
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
import { activeInstance, fetchMapById } from '../../../../redux/clientSlice';
|
||||||
import { SimpleDialogProps, handleOnMutationSuccess } from "..";
|
import { SimpleDialogProps, handleOnMutationSuccess } from "..";
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from "../base-dialog";
|
||||||
@ -14,10 +14,14 @@ const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
|
|
||||||
const mutation = useMutation((id: number) => client.deleteMap(id),
|
const mutation = useMutation((id: number) => client.deleteMap(id),
|
||||||
{
|
{
|
||||||
onSuccess: () => handleOnMutationSuccess(onClose, queryClient)
|
onSuccess: () => handleOnMutationSuccess(onClose, queryClient),
|
||||||
|
onError: (error: ErrorInfo) => {
|
||||||
|
setError(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -35,6 +39,7 @@ const DeleteDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
|
error={error}
|
||||||
onClose={handleOnClose} onSubmit={handleOnSubmit}
|
onClose={handleOnClose} onSubmit={handleOnSubmit}
|
||||||
title={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })}
|
title={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })}
|
||||||
submitButton={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })}>
|
submitButton={intl.formatMessage({ id: "action.delete-title", defaultMessage: "Delete" })}>
|
||||||
|
@ -21,7 +21,10 @@ const DeleteMultiselectDialog = ({ onClose, mapsId }: DeleteMultiselectDialogPro
|
|||||||
|
|
||||||
const mutation = useMutation((ids: number[]) => client.deleteMaps(ids),
|
const mutation = useMutation((ids: number[]) => client.deleteMaps(ids),
|
||||||
{
|
{
|
||||||
onSuccess: () => handleOnMutationSuccess(onClose, queryClient)
|
onSuccess: () => handleOnMutationSuccess(onClose, queryClient),
|
||||||
|
onError: (error) => {
|
||||||
|
console.error(`Unexpected error ${error}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
import { useMutation, useQueryClient } from "react-query";
|
import { useMutation, useQuery, useQueryClient } from "react-query";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import Client from "../../../../classes/client";
|
import Client, { ErrorInfo, Permission } from "../../../../classes/client";
|
||||||
import { activeInstance } from '../../../../redux/clientSlice';
|
import { activeInstance } from '../../../../redux/clientSlice';
|
||||||
import { SimpleDialogProps, handleOnMutationSuccess } from "..";
|
import { SimpleDialogProps } from "..";
|
||||||
import BaseDialog from "../base-dialog";
|
import BaseDialog from "../base-dialog";
|
||||||
import List from "@material-ui/core/List";
|
import List from "@material-ui/core/List";
|
||||||
import ListItem from "@material-ui/core/ListItem";
|
import ListItem from "@material-ui/core/ListItem";
|
||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import ListItemText from "@material-ui/core/ListItemText";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
|
|
||||||
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
|
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
|
||||||
import DeleteIcon from '@material-ui/icons/Delete';
|
import DeleteIcon from '@material-ui/icons/Delete';
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
@ -19,16 +20,41 @@ import Button from "@material-ui/core/Button";
|
|||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||||
import Checkbox from "@material-ui/core/Checkbox";
|
import Checkbox from "@material-ui/core/Checkbox";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
import { useStyles } from "./style";
|
||||||
|
import RoleIcon from "../../role-icon";
|
||||||
|
|
||||||
|
|
||||||
|
type ShareModel = {
|
||||||
|
emails: string,
|
||||||
|
role: 'editor' | 'viewer',
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultModel: ShareModel = { emails: '', role: 'editor', message: '' };
|
||||||
const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const client: Client = useSelector(activeInstance);
|
const client: Client = useSelector(activeInstance);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
const classes = useStyles();
|
||||||
|
const [showMessage, setShowMessage] = React.useState<boolean>(false);
|
||||||
|
const [model, setModel] = React.useState<ShareModel>(defaultModel);
|
||||||
|
const [error, setError] = React.useState<ErrorInfo>();
|
||||||
|
|
||||||
const mutation = useMutation((id: number) => client.deleteMap(id),
|
const mutation = useMutation(
|
||||||
|
(model: ShareModel) => {
|
||||||
|
const emails = model.emails.split("'");
|
||||||
|
const permissions = emails.map((email) => { return { email: email, role: model.role } });
|
||||||
|
return client.addMapPermissions(mapId, model.message, permissions);
|
||||||
|
},
|
||||||
{
|
{
|
||||||
onSuccess: () => handleOnMutationSuccess(onClose, queryClient)
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries(`perm-${mapId}`);
|
||||||
|
setModel(defaultModel);
|
||||||
|
},
|
||||||
|
onError: (error: ErrorInfo) => {
|
||||||
|
setError(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -36,65 +62,117 @@ const ShareDialog = ({ mapId, onClose }: SimpleDialogProps): React.ReactElement
|
|||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnSubmit = (): void => {
|
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
mutation.mutate(mapId);
|
event.preventDefault();
|
||||||
|
|
||||||
|
const name = event.target.name;
|
||||||
|
const value = event.target.value;
|
||||||
|
setModel({ ...model, [name as keyof ShareModel]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch map model to be rendered ...
|
const handleOnClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||||
|
event.stopPropagation();
|
||||||
|
mutation.mutate(model);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { isLoading, data: permissions = [] } = useQuery<unknown, ErrorInfo, Permission[]>(`perm-${mapId}`, () => {
|
||||||
|
return client.fetchMapPermissions(mapId);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
onClose={handleOnClose} onSubmit={handleOnSubmit}
|
onClose={handleOnClose}
|
||||||
title={intl.formatMessage({ id: "share.delete-title", defaultMessage: "Share with people" })}
|
title={intl.formatMessage({ id: "share.delete-title", defaultMessage: "Share with people" })}
|
||||||
description={intl.formatMessage({ id: "share.delete-description", defaultMessage: "Collaboration " })}
|
description={intl.formatMessage({ id: "share.delete-description", defaultMessage: "Invite people to collaborate with you on the creation of your midnmap. They will be notified by email. " })}
|
||||||
submitButton={intl.formatMessage({ id: "share.delete-title", defaultMessage: "Share" })}
|
PaperProps={{ classes: { root: classes.paper } }}
|
||||||
maxWidth="md">
|
error={error}
|
||||||
|
>
|
||||||
|
|
||||||
<div style={{ padding: '10px 10px', background: '#f9f9f9' }}>
|
<div className={classes.actionContainer}>
|
||||||
<TextField id="email" style={{ width: '300px' }} size="small" type="text" variant="outlined" placeholder="Add collaboratos's emails seperated by commas" label="Email" />
|
<TextField
|
||||||
<Select
|
id="emails"
|
||||||
value='edit'
|
name="emails"
|
||||||
|
required={true}
|
||||||
|
style={{ width: '300px' }}
|
||||||
|
size="small"
|
||||||
|
type="email"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
placeholder="Add collaboration email"
|
||||||
|
label="Emails"
|
||||||
|
onChange={handleOnChange}
|
||||||
|
value={model.emails}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleOnChange}
|
||||||
|
value={model.role}
|
||||||
|
name="role"
|
||||||
style={{ margin: '0px 10px' }}
|
style={{ margin: '0px 10px' }}
|
||||||
>
|
>
|
||||||
<MenuItem value='edit'>Can Edit</MenuItem>
|
<MenuItem value='editor'><FormattedMessage id="share.can-edit" defaultMessage="Can edit" /></MenuItem>
|
||||||
<MenuItem value='view'>Can View</MenuItem>
|
<MenuItem value='viewer'><FormattedMessage id="share.can-view" defaultMessage="Can view" /></MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
value="start"
|
value="start"
|
||||||
|
onChange={(event, value) => { setShowMessage(value) }}
|
||||||
|
style={{ fontSize: "5px" }}
|
||||||
control={<Checkbox color="primary" />}
|
control={<Checkbox color="primary" />}
|
||||||
label={<FormattedMessage id="share.add-message" defaultMessage="Add message" />}
|
label={<Typography variant="subtitle2"><FormattedMessage id="share.add-message" defaultMessage="Add message" /></Typography>}
|
||||||
labelPlacement="end"
|
labelPlacement="end"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="contained" disableElevation={true}><FormattedMessage id="share.add-button" defaultMessage="Add " /></Button>
|
type="button"
|
||||||
|
variant="contained"
|
||||||
|
disableElevation={true}
|
||||||
|
onClick={handleOnClick}>
|
||||||
|
<FormattedMessage id="share.add-button" defaultMessage="Add " />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{showMessage &&
|
||||||
|
<TextField
|
||||||
|
multiline
|
||||||
|
rows={3}
|
||||||
|
rowsMax={3}
|
||||||
|
className={classes.textArea}
|
||||||
|
variant="filled"
|
||||||
|
name="message"
|
||||||
|
onChange={handleOnChange}
|
||||||
|
value={model.message}
|
||||||
|
placeholder="Include a customize message to ..."
|
||||||
|
/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Paper elevation={1} style={{ maxHeight: 200, overflowY: 'scroll' }} variant="outlined">
|
{!isLoading &&
|
||||||
<List>
|
<Paper elevation={1} className={classes.listPaper} variant="outlined">
|
||||||
{[.4, 5, 7, 7, 8, 9, 100, 1, 2, 3].map((value) => {
|
<List>
|
||||||
const labelId = `checkbox-list-label-${value}`;
|
{permissions && permissions.map((permission) => {
|
||||||
|
return (
|
||||||
|
<ListItem key={permission.email} role={undefined} dense button>
|
||||||
|
<ListItemText id={`${permission.name}<${permission.email}>`} primary={permission.email} />
|
||||||
|
|
||||||
return (
|
<RoleIcon role={permission.role} />
|
||||||
<ListItem key={value} role={undefined} dense button>
|
|
||||||
<ListItemText id={labelId} primary={`Line item ${value + 1}`} />
|
<ListItemSecondaryAction>
|
||||||
<ListItemSecondaryAction>
|
<IconButton edge="end">
|
||||||
<IconButton edge="end">
|
<DeleteIcon />
|
||||||
<DeleteIcon />
|
</IconButton>
|
||||||
</IconButton>
|
</ListItemSecondaryAction>
|
||||||
</ListItemSecondaryAction>
|
</ListItem>
|
||||||
</ListItem>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
</List>
|
||||||
</List>
|
</Paper>
|
||||||
</Paper>
|
}
|
||||||
|
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
</div >
|
</div >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default ShareDialog;
|
export default ShareDialog;
|
@ -0,0 +1,27 @@
|
|||||||
|
import createStyles from "@material-ui/core/styles/createStyles";
|
||||||
|
import makeStyles from "@material-ui/core/styles/makeStyles";
|
||||||
|
|
||||||
|
export const useStyles = makeStyles(() =>
|
||||||
|
createStyles({
|
||||||
|
actionContainer: {
|
||||||
|
padding: '10px 0px',
|
||||||
|
border: '1px solid rgba(0, 0, 0, 0.12)',
|
||||||
|
borderRadius: '8px 8px 0px 0px',
|
||||||
|
textAlign: "center"
|
||||||
|
},
|
||||||
|
textArea:
|
||||||
|
{
|
||||||
|
width: '730px',
|
||||||
|
margin: '5px 0px',
|
||||||
|
padding: '10px'
|
||||||
|
},
|
||||||
|
listPaper: {
|
||||||
|
maxHeight: 200,
|
||||||
|
overflowY: 'scroll',
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
width: "850px",
|
||||||
|
minWidth: "850px"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
import Help from "@material-ui/icons/Help";
|
import Help from "@material-ui/icons/Help";
|
||||||
import PolicyOutlined from "@material-ui/icons/PolicyOutlined";
|
import PolicyOutlined from "@material-ui/icons/PolicyOutlined";
|
||||||
@ -11,10 +11,12 @@ import Menu from "@material-ui/core/Menu";
|
|||||||
import MenuItem from "@material-ui/core/MenuItem";
|
import MenuItem from "@material-ui/core/MenuItem";
|
||||||
import Link from "@material-ui/core/Link";
|
import Link from "@material-ui/core/Link";
|
||||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||||
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
|
|
||||||
const HelpMenu = (): React.ReactElement => {
|
const HelpMenu = (): React.ReactElement => {
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
@ -26,11 +28,14 @@ const HelpMenu = (): React.ReactElement => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<IconButton
|
<Tooltip arrow={true} title={intl.formatMessage({ id: 'help.support', defaultMessage: 'Support' })}>
|
||||||
aria-haspopup="true"
|
|
||||||
onClick={handleMenu}>
|
<IconButton
|
||||||
<Help />
|
aria-haspopup="true"
|
||||||
</IconButton>
|
onClick={handleMenu}>
|
||||||
|
<Help />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
<Menu id="appbar-profile"
|
<Menu id="appbar-profile"
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
keepMounted
|
keepMounted
|
||||||
|
@ -74,7 +74,10 @@ const MapsPage = (): ReactElement => {
|
|||||||
const mutation = useMutation(
|
const mutation = useMutation(
|
||||||
(id: number) => client.deleteLabel(id),
|
(id: number) => client.deleteLabel(id),
|
||||||
{
|
{
|
||||||
onSuccess: () => queryClient.invalidateQueries('labels')
|
onSuccess: () => queryClient.invalidateQueries('labels'),
|
||||||
|
onError: (error) => {
|
||||||
|
console.error(`Unexpected error ${error}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -87,8 +90,8 @@ const MapsPage = (): ReactElement => {
|
|||||||
mutation.mutate(id);
|
mutation.mutate(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data } = useQuery<unknown, ErrorInfo, Label[]>('labels', async () => {
|
const { data } = useQuery<unknown, ErrorInfo, Label[]>('labels', () => {
|
||||||
return await client.fetchLabels();
|
return client.fetchLabels();
|
||||||
});
|
});
|
||||||
|
|
||||||
const labels: Label[] = data ? data : [];
|
const labels: Label[] = data ? data : [];
|
||||||
@ -140,7 +143,7 @@ const MapsPage = (): ReactElement => {
|
|||||||
elevation={0}>
|
elevation={0}>
|
||||||
|
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Tooltip title={intl.formatMessage({ id: 'maps.create-tooltip', defaultMessage: 'Create a New Map' })}>
|
<Tooltip arrow={true} title={intl.formatMessage({ id: 'maps.create-tooltip', defaultMessage: 'Create a New Map' })}>
|
||||||
<Button color="primary"
|
<Button color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@ -153,7 +156,7 @@ const MapsPage = (): ReactElement => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip title={intl.formatMessage({ id: 'maps.import-desc', defaultMessage: 'Import from other tools' })}>
|
<Tooltip arrow={true} title={intl.formatMessage({ id: 'maps.import-desc', defaultMessage: 'Import from other tools' })}>
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
|
@ -32,6 +32,9 @@ const LanguageMenu = (): React.ReactElement => {
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries('account')
|
queryClient.invalidateQueries('account')
|
||||||
handleClose();
|
handleClose();
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error(`Unexpected error ${error}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -52,7 +55,7 @@ const LanguageMenu = (): React.ReactElement => {
|
|||||||
const accountInfo = fetchAccount();
|
const accountInfo = fetchAccount();
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tooltip title={intl.formatMessage({ id: 'language.change', defaultMessage: 'Change Language' })}>
|
<Tooltip arrow={true} title={intl.formatMessage({ id: 'language.change', defaultMessage: 'Change Language' })}>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
@ -196,7 +196,7 @@ const mapsFilter = (filter: Filter, search: string): ((mapInfo: MapInfo) => bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MapsList = (props: MapsListProps):React.ReactElement => {
|
export const MapsList = (props: MapsListProps): React.ReactElement => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [order, setOrder] = React.useState<Order>('asc');
|
const [order, setOrder] = React.useState<Order>('asc');
|
||||||
const [filter, setFilter] = React.useState<Filter>({ type: 'all' });
|
const [filter, setFilter] = React.useState<Filter>({ type: 'all' });
|
||||||
@ -225,8 +225,8 @@ export const MapsList = (props: MapsListProps):React.ReactElement => {
|
|||||||
}, [props.filter.type, (props.filter as LabelFilter).label]);
|
}, [props.filter.type, (props.filter as LabelFilter).label]);
|
||||||
|
|
||||||
|
|
||||||
const { isLoading, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', async () => {
|
const { isLoading, data } = useQuery<unknown, ErrorInfo, MapInfo[]>('maps', () => {
|
||||||
return await client.fetchAllMaps();
|
return client.fetchAllMaps();
|
||||||
});
|
});
|
||||||
const mapsInfo: MapInfo[] = data ? data.filter(mapsFilter(filter, searchCondition)) : [];
|
const mapsInfo: MapInfo[] = data ? data.filter(mapsFilter(filter, searchCondition)) : [];
|
||||||
|
|
||||||
@ -346,7 +346,7 @@ export const MapsList = (props: MapsListProps):React.ReactElement => {
|
|||||||
|
|
||||||
<div className={classes.toolbarActions}>
|
<div className={classes.toolbarActions}>
|
||||||
{selected.length > 0 &&
|
{selected.length > 0 &&
|
||||||
<Tooltip title="Delete selected">
|
<Tooltip arrow={true} title="Delete selected">
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
@ -362,7 +362,7 @@ export const MapsList = (props: MapsListProps):React.ReactElement => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{selected.length > 0 &&
|
{selected.length > 0 &&
|
||||||
<Tooltip title="Add label to selected">
|
<Tooltip arrow={true} title="Add label to selected">
|
||||||
<Button
|
<Button
|
||||||
color="primary"
|
color="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
@ -456,7 +456,7 @@ export const MapsList = (props: MapsListProps):React.ReactElement => {
|
|||||||
<TableCell
|
<TableCell
|
||||||
padding="checkbox"
|
padding="checkbox"
|
||||||
className={classes.bodyCell}>
|
className={classes.bodyCell}>
|
||||||
<Tooltip title="Starred">
|
<Tooltip arrow={true} title="Starred">
|
||||||
<IconButton aria-label="Starred" size="small" onClick={(e) => handleStarred(e, row.id)}>
|
<IconButton aria-label="Starred" size="small" onClick={(e) => handleStarred(e, row.id)}>
|
||||||
<StarRateRoundedIcon color="action" style={{ color: row.starred ? 'yellow' : 'gray' }} />
|
<StarRateRoundedIcon color="action" style={{ color: row.starred ? 'yellow' : 'gray' }} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@ -464,7 +464,7 @@ export const MapsList = (props: MapsListProps):React.ReactElement => {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell className={classes.bodyCell}>
|
<TableCell className={classes.bodyCell}>
|
||||||
<Tooltip title="Open for edition" placement="bottom-start">
|
<Tooltip arrow={true} title="Open for edition" placement="bottom-start">
|
||||||
<Link href={`/c/maps/${row.id}/edit`} color="textPrimary" underline="always" onClick={(e) => e.stopPropagation()}>
|
<Link href={`/c/maps/${row.id}/edit`} color="textPrimary" underline="always" onClick={(e) => e.stopPropagation()}>
|
||||||
{row.title}
|
{row.title}
|
||||||
</Link>
|
</Link>
|
||||||
@ -480,7 +480,7 @@ export const MapsList = (props: MapsListProps):React.ReactElement => {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell className={classes.bodyCell}>
|
<TableCell className={classes.bodyCell}>
|
||||||
<Tooltip title={
|
<Tooltip arrow={true} title={
|
||||||
`Modified by ${row.lastModificationBy} on ${dayjs(row.lastModificationTime).format("lll")}`
|
`Modified by ${row.lastModificationBy} on ${dayjs(row.lastModificationTime).format("lll")}`
|
||||||
} placement="bottom-start">
|
} placement="bottom-start">
|
||||||
<span>{dayjs(row.lastModificationTime).fromNow()}</span>
|
<span>{dayjs(row.lastModificationTime).fromNow()}</span>
|
||||||
@ -488,7 +488,7 @@ export const MapsList = (props: MapsListProps):React.ReactElement => {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
<TableCell className={classes.bodyCell}>
|
<TableCell className={classes.bodyCell}>
|
||||||
<Tooltip title={intl.formatMessage({ id: 'map.more-actions', defaultMessage: 'More Actions' })}>
|
<Tooltip arrow={true} title={intl.formatMessage({ id: 'map.more-actions', defaultMessage: 'More Actions' })}>
|
||||||
<IconButton aria-label="Others" size="small" onClick={handleActionClick(row.id)}>
|
<IconButton aria-label="Others" size="small" onClick={handleActionClick(row.id)}>
|
||||||
<MoreHorizIcon color="action" />
|
<MoreHorizIcon color="action" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
47
packages/webapp/src/components/maps-page/role-icon/index.tsx
Normal file
47
packages/webapp/src/components/maps-page/role-icon/index.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import Tooltip from "@material-ui/core/Tooltip";
|
||||||
|
import PersonSharpIcon from '@material-ui/icons/PersonSharp';
|
||||||
|
import EditSharpIcon from '@material-ui/icons/EditSharp';
|
||||||
|
import VisibilitySharpIcon from '@material-ui/icons/VisibilitySharp';
|
||||||
|
|
||||||
|
import { FormattedMessage } from "react-intl";
|
||||||
|
import { Role } from "../../../classes/client";
|
||||||
|
|
||||||
|
type RoleIconProps = {
|
||||||
|
role: Role;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RoleIcon = ({ role }: RoleIconProps): React.ReactElement => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{role == 'owner' &&
|
||||||
|
<Tooltip
|
||||||
|
title={<FormattedMessage id="role.owner" defaultMessage="Onwer" />}
|
||||||
|
arrow={true}
|
||||||
|
>
|
||||||
|
<PersonSharpIcon />
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ role == 'editor' &&
|
||||||
|
<Tooltip
|
||||||
|
title={<FormattedMessage id="role.editor" defaultMessage="Editor" />}
|
||||||
|
arrow={true}>
|
||||||
|
<EditSharpIcon />
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
|
||||||
|
{role == 'viewer' &&
|
||||||
|
<Tooltip
|
||||||
|
title={<FormattedMessage id="role.viewer" defaultMessage="Viewer" />}
|
||||||
|
arrow={true}>
|
||||||
|
<VisibilitySharpIcon />
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
</span>)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RoleIcon;
|
||||||
|
|
@ -76,7 +76,7 @@ export const fetchMapById = (id: number): MapLoadResult => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const result = data?.find(m => m.id == id);
|
const result = data?.find(m => m.id == id);
|
||||||
const map = result ? result : null;
|
const map = result || null;
|
||||||
return { isLoading: isLoading, error: error, map: map };
|
return { isLoading: isLoading, error: error, map: map };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user