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) => ( - - )} + + + {({ close }) => } + - + ); } 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 ( -
- - - - - - - - - - - - - {formatMessage(labels.save)} - + + + + + + + + + {onClose && ( - )} - + + {formatMessage(labels.save)} + +
); } 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
-
- - - - - - - - - - + umami + + + + + + + - {formatMessage(labels.login)} - +
-
+ ); } 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; }