Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Mike Cao 2024-02-23 20:41:36 -08:00
commit 0c910f2abd
12 changed files with 46 additions and 29 deletions

View File

@ -1,20 +1,25 @@
import PageHeader from 'components/layout/PageHeader'; import PageHeader from 'components/layout/PageHeader';
import { Icon, Icons, Text } from 'react-basics'; import { Icon, Icons, Text } from 'react-basics';
import { useMessages, useTeamUrl } from 'components/hooks'; import { useLogin, useMessages, useTeamUrl } from 'components/hooks';
import LinkButton from 'components/common/LinkButton'; import LinkButton from 'components/common/LinkButton';
import { ROLES } from 'lib/constants';
export function ReportsHeader() { export function ReportsHeader() {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { renderTeamUrl } = useTeamUrl(); const { renderTeamUrl } = useTeamUrl();
const { user } = useLogin();
const canEdit = user.role !== ROLES.viewOnly;
return ( return (
<PageHeader title={formatMessage(labels.reports)}> <PageHeader title={formatMessage(labels.reports)}>
{canEdit && (
<LinkButton href={renderTeamUrl('/reports/create')} variant="primary"> <LinkButton href={renderTeamUrl('/reports/create')} variant="primary">
<Icon> <Icon>
<Icons.Plus /> <Icons.Plus />
</Icon> </Icon>
<Text>{formatMessage(labels.createReport)}</Text> <Text>{formatMessage(labels.createReport)}</Text>
</LinkButton> </LinkButton>
)}
</PageHeader> </PageHeader>
); );
} }

View File

@ -11,9 +11,9 @@ export function TeamMembersPage({ teamId }: { teamId: string }) {
const { user } = useLogin(); const { user } = useLogin();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const canEdit = team?.teamUser?.find( const canEdit =
({ userId, role }) => role === ROLES.teamOwner && userId === user.id, team?.teamUser?.find(({ userId, role }) => role === ROLES.teamOwner && userId === user.id) &&
); user.role !== ROLES.viewOnly;
return ( return (
<> <>

View File

@ -15,9 +15,9 @@ export function TeamDetails({ teamId }: { teamId: string }) {
const { user } = useLogin(); const { user } = useLogin();
const [tab, setTab] = useState('details'); const [tab, setTab] = useState('details');
const canEdit = !!team?.teamUser?.find( const canEdit =
({ userId, role }) => role === ROLES.teamOwner && userId === user.id, !!team?.teamUser?.find(({ userId, role }) => role === ROLES.teamOwner && userId === user.id) &&
); user.role !== ROLES.viewOnly;
return ( return (
<Flexbox direction="column"> <Flexbox direction="column">

View File

@ -12,16 +12,17 @@ export function TeamWebsitesPage({ teamId }: { teamId: string }) {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { user } = useLogin(); const { user } = useLogin();
const allowEdit = !!team?.teamUser?.find( const canEdit =
!!team?.teamUser?.find(
({ userId, role }) => userId === user.id && role !== ROLES.teamViewOnly, ({ userId, role }) => userId === user.id && role !== ROLES.teamViewOnly,
); ) && user.role !== ROLES.viewOnly;
return ( return (
<> <>
<PageHeader title={formatMessage(labels.websites)}> <PageHeader title={formatMessage(labels.websites)}>
{allowEdit && <WebsiteAddButton teamId={teamId} />} {canEdit && <WebsiteAddButton teamId={teamId} />}
</PageHeader> </PageHeader>
<TeamWebsitesDataTable teamId={teamId} allowEdit={allowEdit} /> <TeamWebsitesDataTable teamId={teamId} allowEdit={canEdit} />
</> </>
); );
} }

View File

@ -13,12 +13,15 @@ export function TeamWebsitesTable({
allowEdit?: boolean; allowEdit?: boolean;
}) { }) {
const { user } = useLogin(); const { user } = useLogin();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
return ( return (
<GridTable data={data}> <GridTable data={data}>
<GridColumn name="name" label={formatMessage(labels.name)} /> <GridColumn name="name" label={formatMessage(labels.name)} />
<GridColumn name="domain" label={formatMessage(labels.domain)} /> <GridColumn name="domain" label={formatMessage(labels.domain)} />
<GridColumn name="createdBy" label={formatMessage(labels.createdBy)}>
{row => row?.createUser?.username}
</GridColumn>
<GridColumn name="action" label=" " alignment="end"> <GridColumn name="action" label=" " alignment="end">
{row => { {row => {
const { id: websiteId } = row; const { id: websiteId } = row;

View File

@ -1,11 +1,16 @@
'use client'; 'use client';
import { useLogin } from 'components/hooks';
import WebsitesDataTable from './WebsitesDataTable'; import WebsitesDataTable from './WebsitesDataTable';
import WebsitesHeader from './WebsitesHeader'; import WebsitesHeader from './WebsitesHeader';
import { ROLES } from 'lib/constants';
export default function WebsitesSettingsPage({ teamId }: { teamId: string }) { export default function WebsitesSettingsPage({ teamId }: { teamId: string }) {
const { user } = useLogin();
const canCreate = user.role !== ROLES.viewOnly;
return ( return (
<> <>
<WebsitesHeader teamId={teamId} /> <WebsitesHeader teamId={teamId} allowCreate={canCreate} />
<WebsitesDataTable teamId={teamId} /> <WebsitesDataTable teamId={teamId} />
</> </>
); );

View File

@ -1,14 +1,11 @@
'use client'; 'use client';
import WebsitesHeader from 'app/(main)/settings/websites/WebsitesHeader'; import WebsitesHeader from 'app/(main)/settings/websites/WebsitesHeader';
import WebsitesDataTable from 'app/(main)/settings/websites/WebsitesDataTable'; import WebsitesDataTable from 'app/(main)/settings/websites/WebsitesDataTable';
import { useLogin } from 'components/hooks';
export default function WebsitesPage({ teamId }: { teamId: string; userId: string }) {
const { user } = useLogin();
export default function WebsitesPage({ teamId }: { teamId: string }) {
return ( return (
<> <>
<WebsitesHeader teamId={teamId} allowCreate={user.role !== 'view-only'} /> <WebsitesHeader teamId={teamId} allowCreate={false} />
<WebsitesDataTable teamId={teamId} allowEdit={false} /> <WebsitesDataTable teamId={teamId} allowEdit={false} />
</> </>
); );

View File

@ -26,6 +26,7 @@ export const labels = defineMessages({
myWebsites: { id: 'label.my-websites', defaultMessage: 'My websites' }, myWebsites: { id: 'label.my-websites', defaultMessage: 'My websites' },
teamWebsites: { id: 'label.team-websites', defaultMessage: 'Team websites' }, teamWebsites: { id: 'label.team-websites', defaultMessage: 'Team websites' },
created: { id: 'label.created', defaultMessage: 'Created' }, created: { id: 'label.created', defaultMessage: 'Created' },
createdBy: { id: 'label.created-by', defaultMessage: 'Created By' },
edit: { id: 'label.edit', defaultMessage: 'Edit' }, edit: { id: 'label.edit', defaultMessage: 'Edit' },
name: { id: 'label.name', defaultMessage: 'Name' }, name: { id: 'label.name', defaultMessage: 'Name' },
member: { id: 'label.member', defaultMessage: 'Member' }, member: { id: 'label.member', defaultMessage: 'Member' },

View File

@ -1,4 +1,4 @@
import { isValid, parseISO } from 'date-fns'; import { isValid } from 'date-fns';
import { DATA_TYPE } from './constants'; import { DATA_TYPE } from './constants';
import { DynamicDataType } from './types'; import { DynamicDataType } from './types';
@ -28,7 +28,7 @@ export function flattenJSON(
export function getDataType(value: any): string { export function getDataType(value: any): string {
let type: string = typeof value; let type: string = typeof value;
if ((type === 'string' && isValid(value)) || isValid(parseISO(value))) { if ((type === 'string' && isValid(value)) || isValid(new Date(value))) {
type = 'date'; type = 'date';
} }

View File

@ -6,7 +6,7 @@ import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, unauthorized } from 'next-basics'; import { methodNotAllowed, ok, unauthorized } from 'next-basics';
import { createReport, getReports } from 'queries'; import { createReport, getReports } from 'queries';
import * as yup from 'yup'; import * as yup from 'yup';
import { canViewTeam, canViewWebsite } from 'lib/auth'; import { canUpdateWebsite, canViewTeam, canViewWebsite } from 'lib/auth';
export interface ReportRequestBody { export interface ReportRequestBody {
websiteId: string; websiteId: string;
@ -89,6 +89,10 @@ export default async (
if (req.method === 'POST') { if (req.method === 'POST') {
const { websiteId, type, name, description, parameters } = req.body; const { websiteId, type, name, description, parameters } = req.body;
if (!(await canUpdateWebsite(req.auth, websiteId))) {
return unauthorized(res);
}
const result = await createReport({ const result = await createReport({
id: uuid(), id: uuid(),
userId, userId,

View File

@ -62,6 +62,7 @@ export default async (
const data: any = { const data: any = {
id: uuid(), id: uuid(),
createdBy: userId,
name, name,
domain, domain,
shareId, shareId,

View File

@ -87,7 +87,7 @@ export async function getTeamWebsites(
teamId, teamId,
}, },
include: { include: {
user: { createUser: {
select: { select: {
id: true, id: true,
username: true, username: true,