diff --git a/README.md b/README.md index ce9f2bfe..1adfbfb3 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ DATABASE_URL=connection-url ``` The connection url is in the following format: + ``` postgresql://username:mypassword@localhost:5432/mydb @@ -48,13 +49,7 @@ mysql://username:mypassword@localhost:3306/mydb yarn build ``` -### Create database tables - -```bash -yarn update-db -``` - -This will also create a login account with username **admin** and password **umami**. +The build step will also create tables in your database if you ae installing for the first time. It will also create a login user with username **admin** and password **umami**. ### Start the application @@ -75,13 +70,15 @@ docker compose up ``` Alternatively, to pull just the Umami Docker image with PostgreSQL support: + ```bash -docker pull docker.umami.is/umami-software/umami:postgresql-latest +docker pull docker.umami.dev/umami-software/umami:postgresql-latest ``` Or with MySQL support: + ```bash -docker pull docker.umami.is/umami-software/umami:mysql-latest +docker pull docker.umami.dev/umami-software/umami:mysql-latest ``` ## Getting updates diff --git a/assets/arrow-right.svg b/assets/arrow-right.svg index 6fc93909..efc5d74a 100644 --- a/assets/arrow-right.svg +++ b/assets/arrow-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/arrow-up-right-from-square.svg b/assets/arrow-up-right-from-square.svg index 90ad457f..8f6de672 100644 --- a/assets/arrow-up-right-from-square.svg +++ b/assets/arrow-up-right-from-square.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/bars.svg b/assets/bars.svg index fdb2d6e4..ba383fa4 100644 --- a/assets/bars.svg +++ b/assets/bars.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/chart-bar.svg b/assets/chart-bar.svg index d1d72fdc..36820b76 100644 --- a/assets/chart-bar.svg +++ b/assets/chart-bar.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/check.svg b/assets/check.svg index 1a7abdce..65810c19 100644 --- a/assets/check.svg +++ b/assets/check.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/chevron-down.svg b/assets/chevron-down.svg index cb9d8fe1..69add632 100644 --- a/assets/chevron-down.svg +++ b/assets/chevron-down.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/code.svg b/assets/code.svg index cd29765e..0f8e0814 100644 --- a/assets/code.svg +++ b/assets/code.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/external-link.svg b/assets/external-link.svg index ed09306f..e24896b0 100644 --- a/assets/external-link.svg +++ b/assets/external-link.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/gear.svg b/assets/gear.svg index ab97a693..47805d46 100644 --- a/assets/gear.svg +++ b/assets/gear.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/logo.svg b/assets/logo.svg index f0e52261..d2c71326 100644 --- a/assets/logo.svg +++ b/assets/logo.svg @@ -1,2 +1 @@ - - + \ No newline at end of file diff --git a/assets/moon.svg b/assets/moon.svg index 6c8955ae..638286fd 100644 --- a/assets/moon.svg +++ b/assets/moon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/pen.svg b/assets/pen.svg index 426c520c..b2979420 100644 --- a/assets/pen.svg +++ b/assets/pen.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/sun.svg b/assets/sun.svg index ebc20eb2..3e487291 100644 --- a/assets/sun.svg +++ b/assets/sun.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/times.svg b/assets/times.svg index c528bcdd..261bb277 100644 --- a/assets/times.svg +++ b/assets/times.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/user.svg b/assets/user.svg index c0094666..62df2c42 100644 --- a/assets/user.svg +++ b/assets/user.svg @@ -1 +1 @@ -Asset 1 \ No newline at end of file + \ No newline at end of file diff --git a/assets/xmark.svg b/assets/xmark.svg index 340f479e..83bd5740 100644 --- a/assets/xmark.svg +++ b/assets/xmark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/components/common/Button.module.css b/components/common/Button.module.css index b911095f..b6edc60e 100644 --- a/components/common/Button.module.css +++ b/components/common/Button.module.css @@ -2,9 +2,9 @@ display: flex; justify-content: center; align-items: center; - font-size: var(--font-size-normal); - color: var(--gray900); - background: var(--gray100); + font-size: var(--font-size-md); + color: var(--base900); + background: var(--base100); padding: 8px 16px; border-radius: 4px; border: 0; @@ -14,11 +14,11 @@ } .button:hover { - background: var(--gray200); + background: var(--base200); } .button:active { - color: var(--gray900); + color: var(--base900); } .label { @@ -29,30 +29,30 @@ } .large { - font-size: var(--font-size-large); + font-size: var(--font-size-lg); } .small { - font-size: var(--font-size-small); + font-size: var(--font-size-sm); } .xsmall { - font-size: var(--font-size-xsmall); + font-size: var(--font-size-xs); } .action, .action:active { - color: var(--gray50); - background: var(--gray900); + color: var(--base50); + background: var(--base900); } .action:hover { - background: var(--gray800); + background: var(--base800); } .danger, .danger:active { - color: var(--gray50); + color: var(--base50); background: var(--red500); } @@ -62,7 +62,7 @@ .light, .light:active { - color: var(--gray900); + color: var(--base900); background: transparent; } @@ -85,18 +85,18 @@ .button:disabled { cursor: default; - color: var(--gray500); - background: var(--gray75); + color: var(--base500); + background: var(--base75); } .button:disabled:active { - color: var(--gray500); + color: var(--base500); } .button:disabled:hover { - background: var(--gray75); + background: var(--base75); } .button.light:disabled { - background: var(--gray50); + background: var(--base50); } diff --git a/components/common/ButtonGroup.module.css b/components/common/ButtonGroup.module.css index bc60f8d3..04d33d22 100644 --- a/components/common/ButtonGroup.module.css +++ b/components/common/ButtonGroup.module.css @@ -2,14 +2,14 @@ display: inline-flex; border-radius: 4px; overflow: hidden; - border: 1px solid var(--gray500); + border: 1px solid var(--base500); } .group .button { border-radius: 0; - color: var(--gray800); - background: var(--gray50); - border-left: 1px solid var(--gray500); + color: var(--base800); + background: var(--base50); + border-left: 1px solid var(--base500); padding: 4px 8px; } @@ -18,7 +18,7 @@ } .group .button:hover { - background: var(--gray100); + background: var(--base100); } .group .button + .button { @@ -26,6 +26,6 @@ } .group .button.selected { - color: var(--gray900); + color: var(--base900); font-weight: 600; } diff --git a/components/common/Calendar.module.css b/components/common/Calendar.module.css index 9751cf25..5e50e79a 100644 --- a/components/common/Calendar.module.css +++ b/components/common/Calendar.module.css @@ -1,7 +1,7 @@ .calendar { display: flex; flex-direction: column; - font-size: var(--font-size-small); + font-size: var(--font-size-sm); flex: 1; min-height: 306px; } @@ -12,7 +12,7 @@ } .calendar td { - color: var(--gray800); + color: var(--base800); cursor: pointer; text-align: center; vertical-align: center; @@ -23,17 +23,17 @@ } .calendar td:hover { - border: 1px solid var(--gray300); - background: var(--gray75); + border: 1px solid var(--base300); + background: var(--base75); } .calendar td.faded { - color: var(--gray500); + color: var(--base500); } .calendar td.selected { font-weight: 600; - border: 1px solid var(--gray600); + border: 1px solid var(--base600); } .calendar td.selected:hover { @@ -41,18 +41,18 @@ } .calendar td.disabled { - color: var(--gray400); - background: var(--gray75); + color: var(--base400); + background: var(--base75); } .calendar td.disabled:hover { cursor: default; - background: var(--gray75); + background: var(--base75); border-color: transparent; } .calendar td.faded.disabled { - background: var(--gray100); + background: var(--base100); } .header { @@ -61,7 +61,7 @@ align-items: center; font-weight: 700; line-height: 40px; - font-size: var(--font-size-normal); + font-size: var(--font-size-md); } .body { diff --git a/components/common/Checkbox.module.css b/components/common/Checkbox.module.css index c9a01eac..edd2b776 100644 --- a/components/common/Checkbox.module.css +++ b/components/common/Checkbox.module.css @@ -11,7 +11,7 @@ align-items: center; width: 20px; height: 20px; - border: 1px solid var(--gray500); + border: 1px solid var(--base500); border-radius: 4px; } diff --git a/components/common/Dot.module.css b/components/common/Dot.module.css index 258d6e87..284aa66d 100644 --- a/components/common/Dot.module.css +++ b/components/common/Dot.module.css @@ -1,5 +1,5 @@ .wrapper { - background: var(--gray50); + background: var(--base50); margin-right: 10px; border-radius: 100%; } diff --git a/components/common/Dropdown.module.css b/components/common/Dropdown.module.css index 9738b007..a1877529 100644 --- a/components/common/Dropdown.module.css +++ b/components/common/Dropdown.module.css @@ -3,7 +3,7 @@ display: flex; justify-content: space-between; align-items: center; - border: 1px solid var(--gray500); + border: 1px solid var(--base500); border-radius: 4px; cursor: pointer; } @@ -12,7 +12,7 @@ flex: 1; display: flex; justify-content: space-between; - font-size: var(--font-size-small); + font-size: var(--font-size-sm); flex-wrap: nowrap; white-space: nowrap; padding: 4px 16px; diff --git a/components/common/ErrorMessage.module.css b/components/common/ErrorMessage.module.css index 88769cf5..dd9295d9 100644 --- a/components/common/ErrorMessage.module.css +++ b/components/common/ErrorMessage.module.css @@ -6,7 +6,7 @@ margin: auto; display: flex; z-index: 1; - background-color: var(--gray50); + background-color: var(--base50); padding: 10px; } diff --git a/components/common/EventDataButton.js b/components/common/EventDataButton.js new file mode 100644 index 00000000..2b895840 --- /dev/null +++ b/components/common/EventDataButton.js @@ -0,0 +1,48 @@ +import List from 'assets/list-ul.svg'; +import Modal from 'components/common/Modal'; +import PropTypes from 'prop-types'; +import { useState } from 'react'; +import { FormattedMessage } from 'react-intl'; +import Button from './Button'; +import EventDataForm from 'components/forms/EventDataForm'; +import styles from './EventDataButton.module.css'; + +function EventDataButton({ websiteId }) { + const [showEventData, setShowEventData] = useState(false); + + function handleClick() { + if (!showEventData) { + setShowEventData(true); + } + } + + function handleClose() { + setShowEventData(false); + } + + return ( + <> + + {showEventData && ( + }> + + + )} + + ); +} + +EventDataButton.propTypes = { + websiteId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +}; + +export default EventDataButton; diff --git a/components/common/EventDataButton.module.css b/components/common/EventDataButton.module.css new file mode 100644 index 00000000..cd2a2ed6 --- /dev/null +++ b/components/common/EventDataButton.module.css @@ -0,0 +1,3 @@ +.button { + width: fit-content; +} diff --git a/components/common/FilterLink.module.css b/components/common/FilterLink.module.css index 45b049da..ce3ffa5f 100644 --- a/components/common/FilterLink.module.css +++ b/components/common/FilterLink.module.css @@ -4,11 +4,11 @@ } .row .inactive { - color: var(--gray500); + color: var(--base500); } .row .active { - color: var(--gray900); + color: var(--base900); font-weight: 600; } diff --git a/components/common/Link.module.css b/components/common/Link.module.css index dd2f92f9..0476bd92 100644 --- a/components/common/Link.module.css +++ b/components/common/Link.module.css @@ -2,7 +2,7 @@ a.link, a.link:active, a.link:visited { position: relative; - color: var(--gray900); + color: var(--base900); text-decoration: none; display: inline-flex; align-items: center; @@ -17,15 +17,15 @@ a.link:hover span { } a.link.large { - font-size: var(--font-size-large); + font-size: var(--font-size-lg); } a.link.small { - font-size: var(--font-size-small); + font-size: var(--font-size-sm); } a.link.xsmall { - font-size: var(--font-size-xsmall); + font-size: var(--font-size-xs); } a.link .icon + * { diff --git a/components/common/Loading.module.css b/components/common/Loading.module.css index a69559e3..e2d53243 100644 --- a/components/common/Loading.module.css +++ b/components/common/Loading.module.css @@ -25,7 +25,7 @@ height: 100%; width: 100%; z-index: 10; - background: var(--gray400); + background: var(--base400); opacity: 0.4; } @@ -33,13 +33,13 @@ width: 10px; height: 10px; border-radius: 100%; - background: var(--gray400); + background: var(--base400); animation: blink 1.4s infinite; animation-fill-mode: both; } .loading.overlay div { - background: var(--gray900); + background: var(--base900); } .loading div + div { diff --git a/components/common/Menu.module.css b/components/common/Menu.module.css index d2ad2cc4..63491d40 100644 --- a/components/common/Menu.module.css +++ b/components/common/Menu.module.css @@ -1,22 +1,22 @@ .menu { - background: var(--gray50); - border: 1px solid var(--gray500); + background: var(--base50); + border: 1px solid var(--base500); border-radius: 4px; overflow: hidden; z-index: 100; } .option { - font-size: var(--font-size-small); + font-size: var(--font-size-sm); font-weight: normal; - background: var(--gray50); + background: var(--base50); padding: 4px 16px; cursor: pointer; white-space: nowrap; } .option:hover { - background: var(--gray100); + background: var(--base100); } .float { @@ -43,7 +43,7 @@ } .divider { - border-top: 1px solid var(--gray300); + border-top: 1px solid var(--base300); } .selected { diff --git a/components/common/MenuButton.module.css b/components/common/MenuButton.module.css index 7e9dd7e1..a59ae562 100644 --- a/components/common/MenuButton.module.css +++ b/components/common/MenuButton.module.css @@ -10,11 +10,11 @@ } .text { - font-size: var(--font-size-small); + font-size: var(--font-size-sm); } .open, .open:hover { - background: var(--gray50); - border: 1px solid var(--gray500); + background: var(--base50); + border: 1px solid var(--base500); } diff --git a/components/common/MobileMenu.module.css b/components/common/MobileMenu.module.css index 78a440d9..d1a6f19b 100644 --- a/components/common/MobileMenu.module.css +++ b/components/common/MobileMenu.module.css @@ -8,7 +8,7 @@ z-index: 100; display: flex; flex-direction: column; - background-color: var(--gray50); + background-color: var(--base50); overflow: auto; } @@ -21,7 +21,7 @@ } .item { - font-size: var(--font-size-large); + font-size: var(--font-size-lg); } .item + .item { diff --git a/components/common/Modal.module.css b/components/common/Modal.module.css index bf2491c7..09054c55 100644 --- a/components/common/Modal.module.css +++ b/components/common/Modal.module.css @@ -5,7 +5,7 @@ right: 0; bottom: 0; margin: auto; - z-index: 2; + z-index: 3; } .modal:before { @@ -25,12 +25,12 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - background: var(--gray50); + background: var(--base50); min-width: 400px; min-height: 100px; max-width: 100vw; z-index: 1; - border: 1px solid var(--gray300); + border: 1px solid var(--base300); padding: 30px; border-radius: 4px; } diff --git a/components/common/NavMenu.module.css b/components/common/NavMenu.module.css index 7be73973..e0b01945 100644 --- a/components/common/NavMenu.module.css +++ b/components/common/NavMenu.module.css @@ -1,6 +1,6 @@ .menu { - color: var(--gray800); - border: 1px solid var(--gray500); + color: var(--base800); + border: 1px solid var(--base500); border-radius: 4px; overflow: hidden; z-index: 2; @@ -13,10 +13,10 @@ } .option:hover { - background: var(--gray75); + background: var(--base75); } .selected { - color: var(--gray900); + color: var(--base900); font-weight: 600; } diff --git a/components/common/NoData.module.css b/components/common/NoData.module.css index 518fa488..29e9f8eb 100644 --- a/components/common/NoData.module.css +++ b/components/common/NoData.module.css @@ -1,6 +1,6 @@ .container { - color: var(--gray500); - font-size: var(--font-size-normal); + color: var(--base500); + font-size: var(--font-size-md); position: relative; display: flex; align-items: center; diff --git a/components/common/Table.module.css b/components/common/Table.module.css index f92338f8..ba4facc5 100644 --- a/components/common/Table.module.css +++ b/components/common/Table.module.css @@ -5,16 +5,16 @@ .table label { display: none; - font-size: var(--font-size-xsmall); + font-size: var(--font-size-xs); font-weight: bold; } .header { - border-bottom: 1px solid var(--gray300); + border-bottom: 1px solid var(--base300); } .head { - font-size: var(--font-size-small); + font-size: var(--font-size-sm); font-weight: 600; line-height: 40px; } @@ -26,7 +26,7 @@ } .row { - border-bottom: 1px solid var(--gray300); + border-bottom: 1px solid var(--base300); padding: 10px 0; } diff --git a/components/common/Tag.module.css b/components/common/Tag.module.css index 5e145ea1..95023bbf 100644 --- a/components/common/Tag.module.css +++ b/components/common/Tag.module.css @@ -1,6 +1,6 @@ .tag { padding: 2px 4px; - border: 1px solid var(--gray300); + border: 1px solid var(--base300); border-radius: 4px; margin-right: 10px; } diff --git a/components/common/Toast.module.css b/components/common/Toast.module.css index 58677dcb..18b8b44d 100644 --- a/components/common/Toast.module.css +++ b/components/common/Toast.module.css @@ -17,7 +17,7 @@ } .message { - font-size: var(--font-size-normal); + font-size: var(--font-size-md); } .close { diff --git a/components/common/UpdateNotice.module.css b/components/common/UpdateNotice.module.css index 52bac611..5161ea71 100644 --- a/components/common/UpdateNotice.module.css +++ b/components/common/UpdateNotice.module.css @@ -6,7 +6,7 @@ } .message { - font-size: var(--font-size-small); + font-size: var(--font-size-sm); font-weight: 600; flex: 1; text-align: center; diff --git a/components/declarations.d.ts b/components/declarations.d.ts new file mode 100644 index 00000000..31e44ff3 --- /dev/null +++ b/components/declarations.d.ts @@ -0,0 +1,2 @@ +declare module '*.css'; +declare module '*.svg'; diff --git a/components/forms/ChangePasswordForm.js b/components/forms/ChangePasswordForm.js index 4870d523..8942ecf6 100644 --- a/components/forms/ChangePasswordForm.js +++ b/components/forms/ChangePasswordForm.js @@ -9,7 +9,7 @@ import FormLayout, { FormRow, } from 'components/layout/FormLayout'; import useApi from 'hooks/useApi'; -import useUser from '../../hooks/useUser'; +import useUser from 'hooks/useUser'; const initialValues = { current_password: '', @@ -43,13 +43,13 @@ export default function ChangePasswordForm({ values, onSave, onClose }) { const { user } = useUser(); const handleSubmit = async values => { - const { ok, data } = await post(`/accounts/${user.user_id}/password`, values); + const { ok, error } = await post(`/users/${user.id}/password`, values); if (ok) { onSave(); } else { setMessage( - data || , + error || , ); } }; diff --git a/components/forms/DatePickerForm.module.css b/components/forms/DatePickerForm.module.css index 96e2d2ec..92e59bb7 100644 --- a/components/forms/DatePickerForm.module.css +++ b/components/forms/DatePickerForm.module.css @@ -16,7 +16,7 @@ .calendars > div + div { margin-left: 20px; padding-left: 20px; - border-left: 1px solid var(--gray300); + border-left: 1px solid var(--base300); } .filter { diff --git a/components/forms/EventDataForm.js b/components/forms/EventDataForm.js new file mode 100644 index 00000000..e236aa3d --- /dev/null +++ b/components/forms/EventDataForm.js @@ -0,0 +1,262 @@ +import classNames from 'classnames'; +import Button from 'components/common/Button'; +import DateFilter from 'components/common/DateFilter'; +import DropDown from 'components/common/DropDown'; +import FormLayout, { + FormButtons, + FormError, + FormMessage, + FormRow, +} from 'components/layout/FormLayout'; +import DataTable from 'components/metrics/DataTable'; +import FilterTags from 'components/metrics/FilterTags'; +import { Field, Form, Formik } from 'formik'; +import useApi from 'hooks/useApi'; +import useDateRange from 'hooks/useDateRange'; +import { useState, useEffect } from 'react'; +import { FormattedMessage } from 'react-intl'; +import styles from './EventDataForm.module.css'; +import useTimezone from 'hooks/useTimezone'; + +export const filterOptions = [ + { label: 'Count', value: 'count' }, + { label: 'Average', value: 'avg' }, + { label: 'Minimum', value: 'min' }, + { label: 'Maximum', value: 'max' }, + { label: 'Sum', value: 'sum' }, +]; + +export const dateOptions = [ + { label: , value: '1day' }, + { + label: ( + + ), + value: '24hour', + }, + { + label: , + value: '-1day', + }, + { + label: , + value: '1week', + divider: true, + }, + { + label: ( + + ), + value: '7day', + }, + { + label: , + value: '1month', + divider: true, + }, + { + label: ( + + ), + value: '30day', + }, + { + label: ( + + ), + value: '90day', + }, + { label: , value: '1year' }, + { + label: , + value: 'custom', + divider: true, + }, +]; + +export default function EventDataForm({ websiteId, onClose, className }) { + const { post } = useApi(); + const [message, setMessage] = useState(); + const [columns, setColumns] = useState({}); + const [filters, setFilters] = useState({}); + const [data, setData] = useState([]); + const [dateRange, setDateRange] = useDateRange('report'); + const { startDate, endDate, value } = dateRange; + const [timezone] = useTimezone(); + const [isValid, setIsValid] = useState(false); + + useEffect(() => { + if (Object.keys(columns).length > 0) { + setIsValid(true); + } else { + setIsValid(false); + } + }, [columns]); + + const handleAddTag = (value, list, setState, resetForm) => { + setState({ ...list, [`${value.field}`]: value.value }); + resetForm(); + }; + + const handleRemoveTag = (value, list, setState) => { + const newList = { ...list }; + + delete newList[`${value}`]; + + setState(newList); + }; + + const handleSubmit = async () => { + const params = { + website_id: websiteId, + start_at: +startDate, + end_at: +endDate, + timezone, + columns, + filters, + }; + + const { ok, data } = await post(`/websites/${websiteId}/eventdata`, params); + + if (!ok) { + setMessage(); + setData([]); + } else { + setData(data); + setMessage(null); + } + }; + + return ( + <> + {message} +
+
+ +
+ + + + +
+
+ + handleAddTag(value, columns, setColumns, resetForm) + } + > + {({ values, setFieldValue }) => ( +
+ + +
+ + +
+
+ + +
+ setFieldValue('value', value)} + className={styles.dropdown} + name="value" + options={filterOptions} + /> + +
+
+ + + +
+ )} +
+ handleRemoveTag(value, columns, setColumns)} + /> +
+
+ + handleAddTag(value, filters, setFilters, resetForm) + } + > + {({ values }) => ( +
+ + +
+ + +
+
+ + +
+ + +
+
+ + + +
+ )} +
+ handleRemoveTag(value, filters, setFilters)} + /> +
+
+
+
+ +
+
+ + + + + + ); +} diff --git a/components/forms/EventDataForm.module.css b/components/forms/EventDataForm.module.css new file mode 100644 index 00000000..fd0ad290 --- /dev/null +++ b/components/forms/EventDataForm.module.css @@ -0,0 +1,38 @@ +.container { + display: flex; +} + +.form { + border-right: 1px solid var(--base300); + width: 420px; +} + +.filters { + padding: 10px 5px; +} + +.filters + .filters { + border-top: 1px solid var(--base300); + min-height: 250px; +} + +.table { + padding: 10px; + min-height: 430px; + min-width: 400px; +} + +.formButtons { + justify-content: flex-start; + margin-left: 20px; +} + +.dropdown { + min-height: 39px; + min-width: 240px; +} + +.filterTag { + flex-wrap: wrap; + margin: 10px 5px 5px 5px; +} diff --git a/components/forms/Form.module.css b/components/forms/Form.module.css new file mode 100644 index 00000000..9185da43 --- /dev/null +++ b/components/forms/Form.module.css @@ -0,0 +1,63 @@ +.form { + display: flex; + flex-direction: column; + gap: 30px; + width: 300px; + margin: 0 auto; +} + +.header { + font-size: 24px; + font-weight: 700; + text-align: center; + margin: 30px auto; +} + +.info { + text-align: center; + padding: 30px 0; +} + +.footer { + display: flex; + flex-direction: column; + gap: 20px; + font-size: 14px; + text-align: center; + margin: 30px auto; +} + +.footer a { + font-weight: 600; +} + +.buttons { + justify-content: center; +} + +.button { + flex: 1; + justify-content: center; +} + +.error { + width: 600px; + margin: 0 auto 30px; + background: var(--base50); + padding: 16px; + color: var(--red400); + border: 1px solid var(--red400); + border-radius: 5px; + text-align: center; +} + +.success { + width: 600px; + margin: 60px auto; + background: var(--base50); + padding: 16px; + color: var(--green400); + border: 1px solid var(--green400); + border-radius: 5px; + text-align: center; +} diff --git a/components/forms/LoginForm.js b/components/forms/LoginForm.js index 8c8aa09e..aad3f97d 100644 --- a/components/forms/LoginForm.js +++ b/components/forms/LoginForm.js @@ -1,111 +1,57 @@ -import React, { useState } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Formik, Form, Field } from 'formik'; -import { setItem } from 'next-basics'; -import { useRouter } from 'next/router'; -import Button from 'components/common/Button'; -import FormLayout, { +import { useMutation } from '@tanstack/react-query'; +import { + Form, + FormInput, FormButtons, - FormError, - FormMessage, - FormRow, -} from 'components/layout/FormLayout'; -import Icon from 'components/common/Icon'; -import useApi from 'hooks/useApi'; -import { AUTH_TOKEN } from 'lib/constants'; + TextField, + PasswordField, + SubmitButton, + Icon, +} from 'react-basics'; +import { useRouter } from 'next/router'; +import { useApi } from 'next-basics'; import { setUser } from 'store/app'; +import { setAuthToken } from 'lib/client'; import Logo from 'assets/logo.svg'; -import styles from './LoginForm.module.css'; - -const validate = ({ username, password }) => { - const errors = {}; - - if (!username) { - errors.username = ; - } - if (!password) { - errors.password = ; - } - - return errors; -}; +import styles from './Form.module.css'; export default function LoginForm() { - const { post } = useApi(); const router = useRouter(); - const [message, setMessage] = useState(); + const { post } = useApi(); + const { mutate, error, isLoading } = useMutation(data => post('/auth/login', data)); - const handleSubmit = async ({ username, password }) => { - const { ok, status, data } = await post('/auth/login', { - username, - password, + const handleSubmit = async data => { + mutate(data, { + onSuccess: async ({ token, user }) => { + setAuthToken(token); + setUser(user); + + await router.push('/websites'); + }, }); - - if (ok) { - setItem(AUTH_TOKEN, data.token); - - setUser(data.user); - - await router.push('/'); - - return null; - } else { - setMessage( - status === 401 ? ( - - ) : ( - data - ), - ); - } }; return ( - - - {() => ( -
-
- } size="xlarge" className={styles.icon} /> -

umami

-
- - -
- - -
-
- - -
- - -
-
- - - - {message} -
- )} -
-
+ <> +
+ + + +

umami

+
+
+ + + + + + + + + Log in + + +
+ ); } diff --git a/components/forms/ShareUrlForm.js b/components/forms/ShareUrlForm.js index a77d3fbc..9800e54b 100644 --- a/components/forms/ShareUrlForm.js +++ b/components/forms/ShareUrlForm.js @@ -8,7 +8,7 @@ import CopyButton from 'components/common/CopyButton'; export default function TrackingCodeForm({ values, onClose }) { const ref = useRef(); const { basePath } = useRouter(); - const { name, share_id } = values; + const { name, shareId } = values; return ( @@ -27,7 +27,7 @@ export default function TrackingCodeForm({ values, onClose }) { spellCheck={false} defaultValue={`${ document.location.origin - }${basePath}/share/${share_id}/${encodeURIComponent(name)}`} + }${basePath}/share/${shareId}/${encodeURIComponent(name)}`} readOnly /> diff --git a/components/forms/TrackingCodeForm.js b/components/forms/TrackingCodeForm.js index e75260f7..5a098b8d 100644 --- a/components/forms/TrackingCodeForm.js +++ b/components/forms/TrackingCodeForm.js @@ -26,7 +26,7 @@ export default function TrackingCodeForm({ values, onClose }) { rows={3} cols={60} spellCheck={false} - defaultValue={``} readOnly diff --git a/components/forms/AccountEditForm.js b/components/forms/UserEditForm.js similarity index 88% rename from components/forms/AccountEditForm.js rename to components/forms/UserEditForm.js index bedc3b82..0d7e392f 100644 --- a/components/forms/AccountEditForm.js +++ b/components/forms/UserEditForm.js @@ -15,26 +15,26 @@ const initialValues = { password: '', }; -const validate = ({ user_id, username, password }) => { +const validate = ({ id, username, password }) => { const errors = {}; if (!username) { errors.username = ; } - if (!user_id && !password) { + if (!id && !password) { errors.password = ; } return errors; }; -export default function AccountEditForm({ values, onSave, onClose }) { +export default function UserEditForm({ values, onSave, onClose }) { const { post } = useApi(); const [message, setMessage] = useState(); const handleSubmit = async values => { - const { user_id } = values; - const { ok, data } = await post(user_id ? `/accounts/${user_id}` : '/accounts', values); + const { id } = values; + const { ok, data } = await post(id ? `/users/${id}` : '/users', values); if (ok) { onSave(); diff --git a/components/forms/WebsiteEditForm.js b/components/forms/WebsiteEditForm.js index 21daeeaf..491a8bfe 100644 --- a/components/forms/WebsiteEditForm.js +++ b/components/forms/WebsiteEditForm.js @@ -37,18 +37,18 @@ const validate = ({ name, domain }) => { return errors; }; -const OwnerDropDown = ({ user, accounts }) => { +const OwnerDropDown = ({ user, users }) => { const { setFieldValue, values } = useFormikContext(); useEffect(() => { - if (values.user_id != null && values.owner === '') { - setFieldValue('owner', values.user_id.toString()); - } else if (user?.user_id && values.owner === '') { - setFieldValue('owner', user.user_id.toString()); + if (values.userId != null && values.owner === '') { + setFieldValue('owner', values.userId.toString()); + } else if (user?.id && values.owner === '') { + setFieldValue('owner', user.id.toString()); } - }, [accounts, setFieldValue, user, values]); + }, [users, setFieldValue, user, values]); - if (user?.is_admin) { + if (user?.isAdmin) { return (
- {accounts?.map(acc => ( - ))} @@ -73,13 +73,14 @@ const OwnerDropDown = ({ user, accounts }) => { export default function WebsiteEditForm({ values, onSave, onClose }) { const { post } = useApi(); - const { data: accounts } = useFetch(`/accounts`); + const { data: users } = useFetch(`/users`); const { user } = useUser(); const [message, setMessage] = useState(); const handleSubmit = async values => { - const { website_id } = values; - const { ok, data } = await post(website_id ? `/websites/${website_id}` : '/websites', values); + const { id } = values; + + const { ok, data } = await post(id ? `/websites/${id}` : '/websites', values); if (ok) { onSave(); @@ -93,7 +94,7 @@ export default function WebsiteEditForm({ values, onSave, onClose }) { return ( @@ -117,17 +118,17 @@ export default function WebsiteEditForm({ values, onSave, onClose }) { name="domain" type="text" placeholder="example.com" - spellcheck="false" - autocapitalize="off" - autocorrect="off" + spellCheck="false" + autoCapitalize="off" + autoCorrect="off" />
- +