Updated teams button.

This commit is contained in:
Mike Cao 2024-02-16 18:58:15 -08:00
parent 8048bc7866
commit 44393472cf
12 changed files with 46 additions and 55 deletions

View file

@ -8,13 +8,12 @@ import LanguageButton from 'components/input/LanguageButton';
import ProfileButton from 'components/input/ProfileButton'; import ProfileButton from 'components/input/ProfileButton';
import TeamsButton from 'components/input/TeamsButton'; import TeamsButton from 'components/input/TeamsButton';
import Icons from 'components/icons'; import Icons from 'components/icons';
import { useLogin, useMessages, useNavigation, useTeamUrl } from 'components/hooks'; import { useMessages, useNavigation, useTeamUrl } from 'components/hooks';
import styles from './NavBar.module.css'; import styles from './NavBar.module.css';
export function NavBar() { export function NavBar() {
const { user } = useLogin();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { pathname } = useNavigation(); const { pathname, router } = useNavigation();
const { teamId, renderTeamUrl } = useTeamUrl(); const { teamId, renderTeamUrl } = useTeamUrl();
const cloudMode = !!process.env.cloudMode; const cloudMode = !!process.env.cloudMode;
@ -60,6 +59,12 @@ export function NavBar() {
!cloudMode && { label: formatMessage(labels.logout), url: '/logout' }, !cloudMode && { label: formatMessage(labels.logout), url: '/logout' },
].filter(n => n); ].filter(n => n);
const handleTeamChange = (teamId: string) => {
const url = teamId ? `/teams/${teamId}` : '/';
router.push(cloudMode ? `${process.env.cloudUrl}${url}` : url);
};
return ( return (
<div className={styles.navbar}> <div className={styles.navbar}>
<div className={styles.logo}> <div className={styles.logo}>
@ -83,7 +88,7 @@ export function NavBar() {
})} })}
</div> </div>
<div className={styles.actions}> <div className={styles.actions}>
{user?.teams?.length && <TeamsButton teamId={teamId} />} <TeamsButton teamId={teamId} onChange={handleTeamChange} />
<ThemeButton /> <ThemeButton />
<LanguageButton /> <LanguageButton />
<ProfileButton /> <ProfileButton />

View file

@ -1,6 +1,6 @@
import DataTable from 'components/common/DataTable'; import DataTable from 'components/common/DataTable';
import TeamsTable from 'app/(main)/settings/teams/TeamsTable'; import TeamsTable from 'app/(main)/settings/teams/TeamsTable';
import { useTeams } from 'components/hooks'; import { useLogin, useTeams } from 'components/hooks';
export function TeamsDataTable({ export function TeamsDataTable({
allowEdit, allowEdit,
@ -9,7 +9,8 @@ export function TeamsDataTable({
allowEdit?: boolean; allowEdit?: boolean;
showActions?: boolean; showActions?: boolean;
}) { }) {
const queryResult = useTeams(); const { user } = useLogin();
const queryResult = useTeams(user.id);
return ( return (
<DataTable queryResult={queryResult}> <DataTable queryResult={queryResult}>

View file

@ -1,6 +1,6 @@
import { Button, Modal, ModalTrigger, ActionForm } from 'react-basics'; import { Button, Modal, ModalTrigger, ActionForm } from 'react-basics';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useLogin, useMessages, useModified, useTeamUrl } from 'components/hooks'; import { useLogin, useMessages, useModified, useTeams, useTeamUrl } from 'components/hooks';
import WebsiteDeleteForm from './WebsiteDeleteForm'; import WebsiteDeleteForm from './WebsiteDeleteForm';
import WebsiteResetForm from './WebsiteResetForm'; import WebsiteResetForm from './WebsiteResetForm';
import WebsiteTransferForm from './WebsiteTransferForm'; import WebsiteTransferForm from './WebsiteTransferForm';
@ -12,11 +12,12 @@ export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?:
const { touch } = useModified(); const { touch } = useModified();
const { teamId, renderTeamUrl } = useTeamUrl(); const { teamId, renderTeamUrl } = useTeamUrl();
const router = useRouter(); const router = useRouter();
const hasTeams = user?.teams?.length > 0; const { result } = useTeams(user.id);
const hasTeams = result?.data?.length > 0;
const isTeamOwner = const isTeamOwner =
(!teamId && hasTeams) || (!teamId && hasTeams) ||
(hasTeams && (hasTeams &&
user?.teams result?.data
?.find(({ id }) => id === teamId) ?.find(({ id }) => id === teamId)
?.teamUser.find(({ role, userId }) => role === ROLES.teamOwner && userId === user.id)); ?.teamUser.find(({ role, userId }) => role === ROLES.teamOwner && userId === user.id));

View file

@ -1,18 +1,15 @@
import useApi from './useApi'; import useApi from './useApi';
import useFilterQuery from './useFilterQuery'; import useFilterQuery from './useFilterQuery';
import useLogin from './useLogin';
import useModified from '../useModified'; import useModified from '../useModified';
export function useTeams(userId?: string) { export function useTeams(userId: string) {
const { get } = useApi(); const { get } = useApi();
const { user } = useLogin();
const id = userId || user?.id;
const { modified } = useModified(`teams`); const { modified } = useModified(`teams`);
return useFilterQuery({ return useFilterQuery({
queryKey: ['teams', { userId: id, modified }], queryKey: ['teams', { userId, modified }],
queryFn: (params: any) => { queryFn: (params: any) => {
return get(`/teams`, params); return get(`/users/${userId}/teams`, params);
}, },
}); });
} }

View file

@ -13,9 +13,8 @@ export function useWebsites(
return useFilterQuery({ return useFilterQuery({
queryKey: ['websites', { userId, teamId, modified, ...params }], queryKey: ['websites', { userId, teamId, modified, ...params }],
queryFn: (data: any) => { queryFn: () => {
return get(teamId ? `/teams/${teamId}/websites` : `/users/${userId || user.id}/websites`, { return get(teamId ? `/teams/${teamId}/websites` : `/users/${userId || user.id}/websites`, {
...data,
...params, ...params,
}); });
}, },

View file

@ -2,25 +2,30 @@ import { Key } from 'react';
import { Text, Icon, Button, Popup, Menu, Item, PopupTrigger, Flexbox } from 'react-basics'; import { Text, Icon, Button, Popup, Menu, Item, PopupTrigger, Flexbox } from 'react-basics';
import classNames from 'classnames'; import classNames from 'classnames';
import Icons from 'components/icons'; import Icons from 'components/icons';
import { useLogin, useMessages, useNavigation } from 'components/hooks'; import { useLogin, useMessages, useTeams } from 'components/hooks';
import styles from './TeamsButton.module.css'; import styles from './TeamsButton.module.css';
export function TeamsButton({ teamId }: { teamId: string }) { export function TeamsButton({
teamId,
onChange,
}: {
teamId: string;
onChange?: (value: string) => void;
}) {
const { user } = useLogin(); const { user } = useLogin();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { router } = useNavigation(); const { result } = useTeams(user?.id);
const team = user?.teams?.find(({ id }) => id === teamId); const team = result?.data?.find(({ id }) => id === teamId);
const cloudMode = !!process.env.cloudMode;
const handleSelect = (close: () => void, id: Key) => { const handleSelect = (close: () => void, id: Key) => {
if (id !== user.id) { onChange?.(id as string);
router.push(cloudMode ? `${process.env.cloudUrl}/teams/${id}` : `/teams/${id}`);
} else {
router.push('/');
}
close(); close();
}; };
if (!result) {
return null;
}
return ( return (
<PopupTrigger> <PopupTrigger>
<Button className={styles.button} variant="quiet"> <Button className={styles.button} variant="quiet">
@ -40,7 +45,7 @@ export function TeamsButton({ teamId }: { teamId: string }) {
</Flexbox> </Flexbox>
</Item> </Item>
<div className={styles.heading}>{formatMessage(labels.team)}</div> <div className={styles.heading}>{formatMessage(labels.team)}</div>
{user?.teams?.map(({ id, name }) => ( {result?.data?.map(({ id, name }) => (
<Item key={id} className={classNames({ [styles.selected]: id === teamId })}> <Item key={id} className={classNames({ [styles.selected]: id === teamId })}>
<Flexbox gap={10} alignItems="center"> <Flexbox gap={10} alignItems="center">
<Icon> <Icon>

View file

@ -2,16 +2,11 @@ import { NextApiRequestAuth } from 'lib/types';
import { useAuth } from 'lib/middleware'; import { useAuth } from 'lib/middleware';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { ok } from 'next-basics'; import { ok } from 'next-basics';
import { getUserTeams } from 'queries/admin/team';
export default async (req: NextApiRequestAuth, res: NextApiResponse) => { export default async (req: NextApiRequestAuth, res: NextApiResponse) => {
await useAuth(req, res); await useAuth(req, res);
const { user } = req.auth; const { user } = req.auth;
const teams = await getUserTeams(user.id);
user['teams'] = teams.data.map(n => n);
return ok(res, user); return ok(res, user);
}; };

View file

@ -3,7 +3,7 @@ import { NextApiRequestQueryBody } from 'lib/types';
import { pageInfo } from 'lib/schema'; import { pageInfo } from 'lib/schema';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { methodNotAllowed } from 'next-basics'; import { methodNotAllowed } from 'next-basics';
import userTeams from 'pages/api/users/[userId]/teams'; import userTeamsRoute from 'pages/api/users/[userId]/teams';
import * as yup from 'yup'; import * as yup from 'yup';
const schema = { const schema = {
@ -20,7 +20,7 @@ export default async (req: NextApiRequestQueryBody, res: NextApiResponse) => {
if (req.method === 'GET') { if (req.method === 'GET') {
req.query.userId = req.auth.user.id; req.query.userId = req.auth.user.id;
return userTeams(req, res); return userTeamsRoute(req, res);
} }
return methodNotAllowed(res); return methodNotAllowed(res);

View file

@ -3,7 +3,7 @@ import { NextApiRequestQueryBody } from 'lib/types';
import { pageInfo } from 'lib/schema'; import { pageInfo } from 'lib/schema';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { methodNotAllowed } from 'next-basics'; import { methodNotAllowed } from 'next-basics';
import userWebsites from 'pages/api/users/[userId]/websites'; import userWebsitesRoute from 'pages/api/users/[userId]/websites';
import * as yup from 'yup'; import * as yup from 'yup';
const schema = { const schema = {
@ -20,7 +20,7 @@ export default async (req: NextApiRequestQueryBody, res: NextApiResponse) => {
if (req.method === 'GET') { if (req.method === 'GET') {
req.query.userId = req.auth.user.id; req.query.userId = req.auth.user.id;
return userWebsites(req, res); return userWebsitesRoute(req, res);
} }
return methodNotAllowed(res); return methodNotAllowed(res);

View file

@ -1,3 +1,4 @@
import * as yup from 'yup';
import { Team } from '@prisma/client'; import { Team } from '@prisma/client';
import { canCreateTeam } from 'lib/auth'; import { canCreateTeam } from 'lib/auth';
import { uuid } from 'lib/crypto'; import { uuid } from 'lib/crypto';
@ -6,8 +7,7 @@ import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
import { pageInfo } from 'lib/schema'; import { pageInfo } from 'lib/schema';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { getRandomChars, methodNotAllowed, ok, unauthorized } from 'next-basics'; import { getRandomChars, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createTeam, getUserTeams } from 'queries'; import { createTeam } from 'queries';
import * as yup from 'yup';
export interface TeamsRequestQuery extends SearchFilter {} export interface TeamsRequestQuery extends SearchFilter {}
export interface TeamsRequestBody { export interface TeamsRequestBody {
@ -34,18 +34,6 @@ export default async (
user: { id: userId }, user: { id: userId },
} = req.auth; } = req.auth;
if (req.method === 'GET') {
const { page, query, pageSize } = req.query;
const results = await getUserTeams(userId, {
page,
query,
pageSize,
});
return ok(res, results);
}
if (req.method === 'POST') { if (req.method === 'POST') {
if (!(await canCreateTeam(req.auth))) { if (!(await canCreateTeam(req.auth))) {
return unauthorized(res); return unauthorized(res);

View file

@ -32,10 +32,10 @@ export default async (
await useValidate(schema, req, res); await useValidate(schema, req, res);
const { user } = req.auth; const { user } = req.auth;
const { id: userId } = req.query; const { userId } = req.query;
if (req.method === 'GET') { if (req.method === 'GET') {
if (!user.isAdmin && user.id !== userId) { if (!user.isAdmin && (!userId || user.id !== userId)) {
return unauthorized(res); return unauthorized(res);
} }

View file

@ -5,7 +5,7 @@ import { NextApiRequestQueryBody, SearchFilter } from 'lib/types';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics'; import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createWebsite } from 'queries'; import { createWebsite } from 'queries';
import userWebsites from 'pages/api/users/[userId]/websites'; import userWebsitesRoute from 'pages/api/users/[userId]/websites';
import * as yup from 'yup'; import * as yup from 'yup';
import { pageInfo } from 'lib/schema'; import { pageInfo } from 'lib/schema';
@ -47,7 +47,7 @@ export default async (
req.query.userId = userId; req.query.userId = userId;
} }
return userWebsites(req, res); return userWebsitesRoute(req, res);
} }
if (req.method === 'POST') { if (req.method === 'POST') {