diff --git a/components/messages.js b/components/messages.js
index c9ce76b9..d0e929f9 100644
--- a/components/messages.js
+++ b/components/messages.js
@@ -65,6 +65,7 @@ export const labels = defineMessages({
singleDay: { id: 'label.single-day', defaultMessage: 'Single day' },
dateRange: { id: 'label.date-range', defaultMessage: 'Date range' },
viewDetails: { id: 'label.view-details', defaultMessage: 'View details' },
+ deleteTeam: { id: 'label.delete-team', defaultMessage: 'Delete team' },
});
export const messages = defineMessages({
@@ -132,6 +133,10 @@ export const messages = defineMessages({
id: 'message.team-not-found',
defaultMessage: 'Team not found.',
},
+ deleteTeam: {
+ id: 'message.delete-team',
+ defaultMessage: 'To delete this team, type {confirmation} in the box below to confirm.',
+ },
});
export const devices = defineMessages({
diff --git a/components/pages/settings/teams/TeamDeleteForm.js b/components/pages/settings/teams/TeamDeleteForm.js
new file mode 100644
index 00000000..20e76a6b
--- /dev/null
+++ b/components/pages/settings/teams/TeamDeleteForm.js
@@ -0,0 +1,44 @@
+import {
+ Button,
+ Form,
+ FormRow,
+ FormButtons,
+ FormInput,
+ SubmitButton,
+ TextField,
+} from 'react-basics';
+import { useIntl } from 'react-intl';
+import { labels, messages } from 'components/messages';
+import useApi from 'hooks/useApi';
+
+const CONFIRM_VALUE = 'DELETE';
+
+export default function TeamDeleteForm({ teamId, onSave, onClose }) {
+ const { formatMessage } = useIntl();
+ const { del, useMutation } = useApi();
+ const { mutate, error } = useMutation(data => del(`/teams/${teamId}`, data));
+
+ const handleSubmit = async data => {
+ mutate(data, {
+ onSuccess: async () => {
+ onSave();
+ onClose();
+ },
+ });
+ };
+
+ return (
+
+ );
+}
diff --git a/components/pages/settings/teams/TeamEditForm.js b/components/pages/settings/teams/TeamEditForm.js
index ca0e7f2d..ba5b21e2 100644
--- a/components/pages/settings/teams/TeamEditForm.js
+++ b/components/pages/settings/teams/TeamEditForm.js
@@ -16,7 +16,7 @@ import { labels } from 'components/messages';
const generateId = () => getRandomChars(16);
-export default function TeamEditForm({ teamId, data, onSave }) {
+export default function TeamEditForm({ teamId, data, onSave, readOnly }) {
const { formatMessage } = useIntl();
const { post, useMutation } = useApi();
const { mutate, error } = useMutation(data => post(`/teams/${teamId}`, data));
@@ -47,19 +47,26 @@ export default function TeamEditForm({ teamId, data, onSave }) {
-
-
-
+ {!readOnly && (
+
+
+
+ )}
+ {readOnly && data.name}
-
+ {!readOnly && (
+
+ )}
-
- {formatMessage(labels.save)}
-
+ {!readOnly && (
+
+ {formatMessage(labels.save)}
+
+ )}
);
}
diff --git a/components/pages/settings/teams/TeamMembers.js b/components/pages/settings/teams/TeamMembers.js
index c9901e34..73926385 100644
--- a/components/pages/settings/teams/TeamMembers.js
+++ b/components/pages/settings/teams/TeamMembers.js
@@ -2,7 +2,7 @@ import { Loading } from 'react-basics';
import useApi from 'hooks/useApi';
import TeamMembersTable from 'components/pages/settings/teams/TeamMembersTable';
-export default function TeamMembers({ teamId }) {
+export default function TeamMembers({ teamId, readOnly }) {
const { get, useQuery } = useApi();
const { data, isLoading } = useQuery(['teams:users', teamId], () =>
get(`/teams/${teamId}/users`),
@@ -12,5 +12,5 @@ export default function TeamMembers({ teamId }) {
return ;
}
- return ;
+ return ;
}
diff --git a/components/pages/settings/teams/TeamMembersTable.js b/components/pages/settings/teams/TeamMembersTable.js
index 036ca26c..7d66dfa6 100644
--- a/components/pages/settings/teams/TeamMembersTable.js
+++ b/components/pages/settings/teams/TeamMembersTable.js
@@ -16,7 +16,7 @@ import { ROLES } from 'lib/constants';
import { labels } from 'components/messages';
import useUser from 'hooks/useUser';
-export default function TeamMembersTable({ data = [] }) {
+export default function TeamMembersTable({ data = [], readOnly }) {
const { formatMessage } = useIntl();
const { user } = useUser();
@@ -44,9 +44,9 @@ export default function TeamMembersTable({ data = [] }) {
role: formatMessage(
labels[Object.keys(ROLES).find(key => ROLES[key] === row.role) || labels.unknown],
),
- action: (
+ action: !readOnly && (
-
)}
- {hasData && }
+ {hasData && }
{!hasData && (
{createButton}
diff --git a/components/pages/settings/teams/TeamsTable.js b/components/pages/settings/teams/TeamsTable.js
index bc26fd47..8aa91723 100644
--- a/components/pages/settings/teams/TeamsTable.js
+++ b/components/pages/settings/teams/TeamsTable.js
@@ -11,12 +11,15 @@ import {
Flexbox,
Icons,
Text,
+ ModalTrigger,
+ Modal,
} from 'react-basics';
import { useIntl } from 'react-intl';
import { labels } from 'components/messages';
import { ROLES } from 'lib/constants';
+import TeamDeleteForm from './TeamDeleteForm';
-export default function TeamsTable({ data = [] }) {
+export default function TeamsTable({ data = [], onDelete }) {
const { formatMessage } = useIntl();
const columns = [
@@ -44,7 +47,7 @@ export default function TeamsTable({ data = [] }) {
...row,
owner: row.teamUser.find(({ role }) => role === ROLES.teamOwner)?.user?.username,
action: (
-
+
@@ -55,6 +58,17 @@ export default function TeamsTable({ data = [] }) {
+
+
+
+
+
+ {formatMessage(labels.delete)}
+
+
+ {close => }
+
+
),
};
diff --git a/lib/auth.ts b/lib/auth.ts
index 1113ebf5..31b34fe9 100644
--- a/lib/auth.ts
+++ b/lib/auth.ts
@@ -1,4 +1,5 @@
import debug from 'debug';
+import { validate } from 'uuid';
import cache from 'lib/cache';
import { PERMISSIONS, ROLE_PERMISSIONS, SHARE_TOKEN_HEADER } from 'lib/constants';
import { secret } from 'lib/crypto';
@@ -60,10 +61,6 @@ export async function canViewWebsite({ user }: Auth, websiteId: string) {
return user.id === website.userId;
}
- if (website.teamId) {
- return getTeamUser(website.teamId, user.id);
- }
-
return false;
}
@@ -86,18 +83,16 @@ export async function canUpdateWebsite({ user }: Auth, websiteId: string) {
return true;
}
+ if (!validate(websiteId)) {
+ return false;
+ }
+
const website = await cache.fetchWebsite(websiteId);
if (website.userId) {
return user.id === website.userId;
}
- if (website.teamId) {
- const teamUser = await getTeamUser(website.teamId, user.id);
-
- return hasPermission(teamUser.role, PERMISSIONS.websiteUpdate);
- }
-
return false;
}
@@ -112,12 +107,6 @@ export async function canDeleteWebsite({ user }: Auth, websiteId: string) {
return user.id === website.userId;
}
- if (website.teamId) {
- const teamUser = await getTeamUser(website.teamId, user.id);
-
- return hasPermission(teamUser.role, PERMISSIONS.websiteDelete);
- }
-
return false;
}
@@ -144,9 +133,13 @@ export async function canUpdateTeam({ user }: Auth, teamId: string) {
return true;
}
- const teamUser = await getTeamUser(teamId, user.id);
+ if (validate(teamId)) {
+ const teamUser = await getTeamUser(teamId, user.id);
- return hasPermission(teamUser.role, PERMISSIONS.teamUpdate);
+ return hasPermission(teamUser.role, PERMISSIONS.teamUpdate);
+ }
+
+ return false;
}
export async function canDeleteTeam({ user }: Auth, teamId: string) {
@@ -154,9 +147,13 @@ export async function canDeleteTeam({ user }: Auth, teamId: string) {
return true;
}
- const teamUser = await getTeamUser(teamId, user.id);
+ if (validate(teamId)) {
+ const teamUser = await getTeamUser(teamId, user.id);
- return hasPermission(teamUser.role, PERMISSIONS.teamDelete);
+ return hasPermission(teamUser.role, PERMISSIONS.teamDelete);
+ }
+
+ return false;
}
export async function canCreateUser({ user }: Auth) {
diff --git a/lib/constants.ts b/lib/constants.ts
index 4819eb53..57746404 100644
--- a/lib/constants.ts
+++ b/lib/constants.ts
@@ -33,7 +33,6 @@ export const ROLES = {
user: 'user',
teamOwner: 'team-owner',
teamMember: 'team-member',
- teamGuest: 'team-guest',
} as const;
export const PERMISSIONS = {
@@ -54,19 +53,8 @@ export const ROLE_PERMISSIONS = {
PERMISSIONS.websiteDelete,
PERMISSIONS.teamCreate,
],
- [ROLES.teamOwner]: [
- PERMISSIONS.teamUpdate,
- PERMISSIONS.teamDelete,
- PERMISSIONS.websiteCreate,
- PERMISSIONS.websiteUpdate,
- PERMISSIONS.websiteDelete,
- ],
- [ROLES.teamMember]: [
- PERMISSIONS.websiteCreate,
- PERMISSIONS.websiteUpdate,
- PERMISSIONS.websiteDelete,
- ],
- [ROLES.teamGuest]: [],
+ [ROLES.teamOwner]: [PERMISSIONS.teamUpdate, PERMISSIONS.teamDelete],
+ [ROLES.teamMember]: [],
} as const;
export const THEME_COLORS = {
diff --git a/queries/admin/team.ts b/queries/admin/team.ts
index 7d9d7e4e..238aaab3 100644
--- a/queries/admin/team.ts
+++ b/queries/admin/team.ts
@@ -6,6 +6,9 @@ import { ROLES } from 'lib/constants';
export async function getTeam(where: Prisma.TeamWhereInput): Promise {
return prisma.client.team.findFirst({
where,
+ include: {
+ teamUser: true,
+ },
});
}