diff --git a/src/app/(main)/settings/websites/WebsiteAddButton.tsx b/src/app/(main)/settings/websites/WebsiteAddButton.tsx
index 24be990c..bd2a8146 100644
--- a/src/app/(main)/settings/websites/WebsiteAddButton.tsx
+++ b/src/app/(main)/settings/websites/WebsiteAddButton.tsx
@@ -1,31 +1,40 @@
import { useMessages, useModified } from '@/components/hooks';
-import { Button, Icon, Icons, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
+import {
+ Button,
+ Icon,
+ Icons,
+ Modal,
+ Dialog,
+ DialogTrigger,
+ Text,
+ useToast,
+} from '@umami/react-zen';
import { WebsiteAddForm } from './WebsiteAddForm';
export function WebsiteAddButton({ teamId, onSave }: { teamId: string; onSave?: () => void }) {
const { formatMessage, labels, messages } = useMessages();
- const { showToast } = useToasts();
+ const { toast } = useToast();
const { touch } = useModified();
const handleSave = async () => {
- showToast({ message: formatMessage(messages.saved), variant: 'success' });
+ toast(formatMessage(messages.saved));
touch('websites');
onSave?.();
};
return (
-
+
-
- {(close: () => void) => (
-
- )}
+
+
-
+
);
}
diff --git a/src/app/(main)/settings/websites/WebsiteAddForm.tsx b/src/app/(main)/settings/websites/WebsiteAddForm.tsx
index 4fc8d676..b3a95f8e 100644
--- a/src/app/(main)/settings/websites/WebsiteAddForm.tsx
+++ b/src/app/(main)/settings/websites/WebsiteAddForm.tsx
@@ -1,12 +1,4 @@
-import {
- Form,
- FormRow,
- FormInput,
- FormButtons,
- TextField,
- Button,
- SubmitButton,
-} from 'react-basics';
+import { Form, FormField, FormSubmitButton, Row, TextField, Button } from '@umami/react-zen';
import { useApi } from '@/components/hooks';
import { DOMAIN_REGEX } from '@/lib/constants';
import { useMessages } from '@/components/hooks';
@@ -36,38 +28,37 @@ export function WebsiteAddForm({
};
return (
-
);
}
diff --git a/src/app/(main)/settings/websites/WebsitesHeader.tsx b/src/app/(main)/settings/websites/WebsitesHeader.tsx
index 17e3bccb..0d32fbe0 100644
--- a/src/app/(main)/settings/websites/WebsitesHeader.tsx
+++ b/src/app/(main)/settings/websites/WebsitesHeader.tsx
@@ -1,14 +1,14 @@
-import { useMessages } from '@/components/hooks';
+import { useMessages, useTeamUrl } from '@/components/hooks';
import { PageHeader } from '@/components/layout/PageHeader';
import { WebsiteAddButton } from './WebsiteAddButton';
export interface WebsitesHeaderProps {
- teamId?: string;
allowCreate?: boolean;
}
-export function WebsitesHeader({ teamId, allowCreate = true }: WebsitesHeaderProps) {
+export function WebsitesHeader({ allowCreate = true }: WebsitesHeaderProps) {
const { formatMessage, labels } = useMessages();
+ const { teamId } = useTeamUrl();
return (
diff --git a/src/app/(main)/websites/WebsitesPage.tsx b/src/app/(main)/websites/WebsitesPage.tsx
index 98122c61..f2c8c6bd 100644
--- a/src/app/(main)/websites/WebsitesPage.tsx
+++ b/src/app/(main)/websites/WebsitesPage.tsx
@@ -8,7 +8,7 @@ export function WebsitesPage() {
return (
<>
-
+
>
);
diff --git a/src/app/Providers.tsx b/src/app/Providers.tsx
index d8c7f030..ca0d049b 100644
--- a/src/app/Providers.tsx
+++ b/src/app/Providers.tsx
@@ -2,12 +2,12 @@
import { IntlProvider } from 'react-intl';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ZenProvider } from '@umami/react-zen';
-import { ErrorBoundary } from '@/components/common/ErrorBoundary';
-import { useLocale } from '@/components/hooks';
import 'chartjs-adapter-date-fns';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { RouterProvider } from 'react-aria-components';
+import { ErrorBoundary } from '@/components/common/ErrorBoundary';
+import { useLocale } from '@/components/hooks';
const client = new QueryClient({
defaultOptions: {
diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts
index 7ae22e2b..042f2143 100644
--- a/src/app/api/auth/login/route.ts
+++ b/src/app/api/auth/login/route.ts
@@ -26,7 +26,7 @@ export async function POST(request: Request) {
const user = await getUserByUsername(username, { includePassword: true });
if (!user || !checkPassword(password, user.password)) {
- return unauthorized();
+ return unauthorized({ code: 'incorrect-username-password' });
}
const { id, role, createdAt } = user;
diff --git a/src/app/login/LoginForm.module.css b/src/app/login/LoginForm.module.css
deleted file mode 100644
index 0d3f981f..00000000
--- a/src/app/login/LoginForm.module.css
+++ /dev/null
@@ -1,33 +0,0 @@
-.login {
- width: 400px;
- margin: auto;
- transform: translateY(-25%);
-}
-
-.form {
- display: flex;
- flex-direction: column;
- margin: 0 auto;
- width: 300px;
-}
-
-.title {
- font-size: 24px;
- font-weight: 700;
- text-align: center;
- margin: 30px 0;
-}
-
-.icon {
- width: 100%;
-}
-
-.icon svg {
- width: 32px;
- height: 32px;
-}
-
-.button {
- flex: 1;
- justify-content: center;
-}
diff --git a/src/app/login/LoginForm.tsx b/src/app/login/LoginForm.tsx
index a7fbc1c2..e71fe6fc 100644
--- a/src/app/login/LoginForm.tsx
+++ b/src/app/login/LoginForm.tsx
@@ -1,22 +1,22 @@
import {
Form,
- FormRow,
- FormInput,
FormButtons,
+ FormField,
+ FormSubmitButton,
TextField,
PasswordField,
- SubmitButton,
Icon,
-} from 'react-basics';
+ Column,
+ Heading,
+} from '@umami/react-zen';
import { useRouter } from 'next/navigation';
import { useApi, useMessages } from '@/components/hooks';
import { setUser } from '@/store/app';
import { setClientAuthToken } from '@/lib/client';
import Logo from '@/assets/logo.svg';
-import styles from './LoginForm.module.css';
export function LoginForm() {
- const { formatMessage, labels } = useMessages();
+ const { formatMessage, labels, getMessage } = useMessages();
const router = useRouter();
const { post, useMutation } = useApi();
const { mutate, error, isPending } = useMutation({
@@ -35,41 +35,47 @@ export function LoginForm() {
};
return (
-
-
+
+
- umami
-
-
+
);
}
diff --git a/src/app/login/LoginPage.module.css b/src/app/login/LoginPage.module.css
deleted file mode 100644
index 45115d5b..00000000
--- a/src/app/login/LoginPage.module.css
+++ /dev/null
@@ -1,7 +0,0 @@
-.page {
- display: flex;
- align-items: center;
- justify-content: center;
- height: 100vh;
- background: var(--base75);
-}
diff --git a/src/app/login/LoginPage.tsx b/src/app/login/LoginPage.tsx
index 9d4e2875..a4fc9b55 100644
--- a/src/app/login/LoginPage.tsx
+++ b/src/app/login/LoginPage.tsx
@@ -1,6 +1,6 @@
'use client';
+import { Column } from '@umami/react-zen';
import { LoginForm } from './LoginForm';
-import styles from './LoginPage.module.css';
export function LoginPage() {
if (process.env.disableLogin) {
@@ -8,8 +8,8 @@ export function LoginPage() {
}
return (
-
+
-
+
);
}
diff --git a/src/components/hooks/useApi.ts b/src/components/hooks/useApi.ts
index 27cb5de5..17b83e5f 100644
--- a/src/components/hooks/useApi.ts
+++ b/src/components/hooks/useApi.ts
@@ -9,7 +9,8 @@ const selector = (state: { shareToken: { token?: string } }) => state.shareToken
async function handleResponse(res: FetchResponse): Promise {
if (!res.ok) {
- return Promise.reject(new Error(res.error?.error || res.error || 'Unexpectd error.'));
+ const { message, code } = res?.error?.error || {};
+ return Promise.reject(new Error(code || message || 'Unexpectd error.'));
}
return Promise.resolve(res.data);
}
diff --git a/src/components/hooks/useMessages.ts b/src/components/hooks/useMessages.ts
index 2b4752b6..f06f936b 100644
--- a/src/components/hooks/useMessages.ts
+++ b/src/components/hooks/useMessages.ts
@@ -5,7 +5,7 @@ export function useMessages(): any {
const intl = useIntl();
const getMessage = (id: string) => {
- const message = Object.values(messages).find(value => value.id === id);
+ const message = Object.values(messages).find(value => value.id === `message.${id}`);
return message ? formatMessage(message) : id;
};
diff --git a/src/components/input/TeamsButton.tsx b/src/components/input/TeamsButton.tsx
index b7b7438a..92664d0d 100644
--- a/src/components/input/TeamsButton.tsx
+++ b/src/components/input/TeamsButton.tsx
@@ -53,7 +53,7 @@ export function TeamsButton({
{teamId ? : }
{showText && {teamId ? team?.name : user.username}}
-
+
diff --git a/src/components/layout/PageHeader.tsx b/src/components/layout/PageHeader.tsx
index 7a1f842e..db1608cd 100644
--- a/src/components/layout/PageHeader.tsx
+++ b/src/components/layout/PageHeader.tsx
@@ -1,12 +1,9 @@
-import classNames from 'classnames';
-import React, { ReactNode } from 'react';
-import { Icon } from 'react-basics';
-import styles from './PageHeader.module.css';
+import { ReactNode } from 'react';
+import { Heading, Icon, Breadcrumbs, Breadcrumb, Row } from '@umami/react-zen';
export function PageHeader({
title,
icon,
- className,
breadcrumb,
children,
}: {
@@ -18,17 +15,15 @@ export function PageHeader({
}) {
return (
<>
- {breadcrumb}
-
- {icon && (
-
- {icon}
-
- )}
+
+ {breadcrumb}
+
+
+ {icon && {icon}}
- {title && {title}
}
- {children}
-
+ {title && {title}}
+ {children}
+
>
);
}
diff --git a/src/components/messages.ts b/src/components/messages.ts
index 4de12c39..69810836 100644
--- a/src/components/messages.ts
+++ b/src/components/messages.ts
@@ -429,4 +429,24 @@ export const messages = defineMessages({
id: 'message.transfer-user-website-to-team',
defaultMessage: 'Select the team to transfer this website to.',
},
+ unauthorized: {
+ id: 'message.unauthorized',
+ defaultMessage: 'Unauthorized',
+ },
+ badRequest: {
+ id: 'message.bad-request',
+ defaultMessage: 'Bad request',
+ },
+ forbidden: {
+ id: 'message.forbidden',
+ defaultMessage: 'Forbidden',
+ },
+ notFound: {
+ id: 'message.not-found',
+ defaultMessage: 'Not found',
+ },
+ serverError: {
+ id: 'message.sever-error',
+ defaultMessage: 'Server error',
+ },
});
diff --git a/src/lib/response.ts b/src/lib/response.ts
index d50b453c..6a5860dc 100644
--- a/src/lib/response.ts
+++ b/src/lib/response.ts
@@ -1,5 +1,3 @@
-import { serializeError } from 'serialize-error';
-
export function ok() {
return Response.json({ ok: true });
}
@@ -8,22 +6,53 @@ export function json(data: any) {
return Response.json(data);
}
-export function badRequest(error: any = 'Bad request') {
- return Response.json({ error: serializeError(error) }, { status: 400 });
+export function badRequest(error?: any) {
+ return Response.json(
+ {
+ error: { message: 'Bad request', code: 'bad-request', status: 400, ...error },
+ },
+ { status: 400 },
+ );
}
-export function unauthorized(error: any = 'Unauthorized') {
- return Response.json({ error: serializeError(error) }, { status: 401 });
+export function unauthorized(error?: any) {
+ return Response.json(
+ {
+ error: {
+ message: 'Unauthorized',
+ code: 'unauthorized',
+ status: 401,
+ ...error,
+ },
+ },
+ { status: 401 },
+ );
}
-export function forbidden(error: any = 'Forbidden') {
- return Response.json({ error: serializeError(error) }, { status: 403 });
+export function forbidden(error?: any) {
+ return Response.json(
+ { error: { message: 'Forbidden', code: 'forbidden', status: 403, ...error } },
+ { status: 403 },
+ );
}
-export function notFound(error: any = 'Not found') {
- return Response.json({ error: serializeError(error) }, { status: 404 });
+export function notFound(error?: any) {
+ return Response.json(
+ { error: { message: 'Not found', code: 'not-found', status: 404, ...error } },
+ { status: 404 },
+ );
}
-export function serverError(error: any = 'Server error') {
- return Response.json({ error: serializeError(error) }, { status: 500 });
+export function serverError(error?: any) {
+ return Response.json(
+ {
+ error: {
+ message: 'Server error',
+ code: 'server-error',
+ status: 500,
+ ...error,
+ },
+ },
+ { status: 500 },
+ );
}
diff --git a/src/styles/variables.css b/src/styles/variables.css
index 5999c712..7852d78b 100644
--- a/src/styles/variables.css
+++ b/src/styles/variables.css
@@ -1,3 +1,3 @@
html body {
- --primary-color: var(--accent-color-blue);
+ --primary-color: #147af3;
}