diff --git a/assets/buoy.svg b/assets/buoy.svg new file mode 100644 index 00000000..847a814e --- /dev/null +++ b/assets/buoy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/card.svg b/assets/card.svg new file mode 100644 index 00000000..ecda0e46 --- /dev/null +++ b/assets/card.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/users.svg b/assets/users.svg new file mode 100644 index 00000000..f775ea91 --- /dev/null +++ b/assets/users.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/website.svg b/assets/website.svg new file mode 100644 index 00000000..cfa9e565 --- /dev/null +++ b/assets/website.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/common/Button.js b/components/common/Button.js deleted file mode 100644 index d3b7d2ad..00000000 --- a/components/common/Button.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import ReactTooltip from 'react-tooltip'; -import classNames from 'classnames'; -import Icon from './Icon'; -import styles from './Button.module.css'; - -function Button({ - type = 'button', - icon, - size, - variant, - children, - className, - tooltip, - tooltipId, - disabled, - iconRight, - onClick = () => {}, - ...props -}) { - return ( - - ); -} - -Button.propTypes = { - type: PropTypes.oneOf(['button', 'submit', 'reset']), - icon: PropTypes.node, - size: PropTypes.oneOf(['xlarge', 'large', 'medium', 'small', 'xsmall']), - variant: PropTypes.oneOf(['action', 'danger', 'light']), - children: PropTypes.node, - className: PropTypes.string, - tooltip: PropTypes.node, - tooltipId: PropTypes.string, - disabled: PropTypes.bool, - iconRight: PropTypes.bool, - onClick: PropTypes.func, -}; - -export default Button; diff --git a/components/common/Button.module.css b/components/common/Button.module.css deleted file mode 100644 index b6edc60e..00000000 --- a/components/common/Button.module.css +++ /dev/null @@ -1,102 +0,0 @@ -.button { - display: flex; - justify-content: center; - align-items: center; - font-size: var(--font-size-md); - color: var(--base900); - background: var(--base100); - padding: 8px 16px; - border-radius: 4px; - border: 0; - outline: none; - cursor: pointer; - position: relative; -} - -.button:hover { - background: var(--base200); -} - -.button:active { - color: var(--base900); -} - -.label { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - max-width: 300px; -} - -.large { - font-size: var(--font-size-lg); -} - -.small { - font-size: var(--font-size-sm); -} - -.xsmall { - font-size: var(--font-size-xs); -} - -.action, -.action:active { - color: var(--base50); - background: var(--base900); -} - -.action:hover { - background: var(--base800); -} - -.danger, -.danger:active { - color: var(--base50); - background: var(--red500); -} - -.danger:hover { - background: var(--red400); -} - -.light, -.light:active { - color: var(--base900); - background: transparent; -} - -.light:hover { - background: inherit; -} - -.button .icon + * { - margin-left: 10px; -} - -.button.iconRight .icon { - order: 1; - margin-left: 10px; -} - -.button.iconRight .icon + * { - margin: 0; -} - -.button:disabled { - cursor: default; - color: var(--base500); - background: var(--base75); -} - -.button:disabled:active { - color: var(--base500); -} - -.button:disabled:hover { - background: var(--base75); -} - -.button.light:disabled { - background: var(--base50); -} diff --git a/components/common/ButtonGroup.js b/components/common/ButtonGroup.js deleted file mode 100644 index 353ce690..00000000 --- a/components/common/ButtonGroup.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import Button from './Button'; -import styles from './ButtonGroup.module.css'; - -function ButtonGroup({ items = [], selectedItem, className, size, icon, onClick = () => {} }) { - return ( -
- {items.map(item => { - const { label, value } = item; - return ( - - ); - })} -
- ); -} - -ButtonGroup.propTypes = { - items: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.node, - value: PropTypes.any.isRequired, - }), - ), - selectedItem: PropTypes.any, - className: PropTypes.string, - size: PropTypes.oneOf(['xlarge', 'large', 'medium', 'small', 'xsmall']), - icon: PropTypes.node, - onClick: PropTypes.func, -}; - -export default ButtonGroup; diff --git a/components/common/ButtonGroup.module.css b/components/common/ButtonGroup.module.css deleted file mode 100644 index 04d33d22..00000000 --- a/components/common/ButtonGroup.module.css +++ /dev/null @@ -1,31 +0,0 @@ -.group { - display: inline-flex; - border-radius: 4px; - overflow: hidden; - border: 1px solid var(--base500); -} - -.group .button { - border-radius: 0; - color: var(--base800); - background: var(--base50); - border-left: 1px solid var(--base500); - padding: 4px 8px; -} - -.group .button:first-child { - border: 0; -} - -.group .button:hover { - background: var(--base100); -} - -.group .button + .button { - margin: 0; -} - -.group .button.selected { - color: var(--base900); - font-weight: 600; -} diff --git a/components/common/Calendar.js b/components/common/Calendar.js index b6c5cd0b..077382d1 100644 --- a/components/common/Calendar.js +++ b/components/common/Calendar.js @@ -16,7 +16,7 @@ import { isBefore, isAfter, } from 'date-fns'; -import Button from './Button'; +import { Button, Icon } from 'react-basics'; import useLocale from 'hooks/useLocale'; import { dateFormat } from 'lib/date'; import { chunk } from 'lib/array'; @@ -24,7 +24,6 @@ import { getDateLocale } from 'lib/lang'; import Chevron from 'assets/chevron-down.svg'; import Cross from 'assets/times.svg'; import styles from './Calendar.module.css'; -import Icon from './Icon'; export default function Calendar({ date, minDate, maxDate, onChange }) { const { locale } = useLocale(); @@ -61,14 +60,18 @@ export default function Calendar({ date, minDate, maxDate, onChange }) { onClick={toggleMonthSelect} > {month} - : } size="small" /> + + {selectMonth ? : } +
{year} - : } size="small" /> + + {selectMonth ? : } +
@@ -230,12 +233,15 @@ const YearSelector = ({ date, minDate, maxDate, onSelect }) => {
@@ -261,12 +267,15 @@ const YearSelector = ({ date, minDate, maxDate, onSelect }) => {
); diff --git a/components/common/Checkbox.js b/components/common/Checkbox.js deleted file mode 100644 index 0cd0dcad..00000000 --- a/components/common/Checkbox.js +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useRef } from 'react'; -import PropTypes from 'prop-types'; -import Icon from 'components/common/Icon'; -import Check from 'assets/check.svg'; -import styles from './Checkbox.module.css'; - -function Checkbox({ name, value, label, onChange }) { - const ref = useRef(); - - const onClick = () => ref.current.click(); - - return ( -
-
- {value && } size="small" />} -
- - -
- ); -} - -Checkbox.propTypes = { - name: PropTypes.string, - value: PropTypes.any, - label: PropTypes.node, - onChange: PropTypes.func, -}; - -export default Checkbox; diff --git a/components/common/Checkbox.module.css b/components/common/Checkbox.module.css deleted file mode 100644 index edd2b776..00000000 --- a/components/common/Checkbox.module.css +++ /dev/null @@ -1,30 +0,0 @@ -.container { - display: flex; - align-items: center; - position: relative; - overflow: hidden; -} - -.checkbox { - display: flex; - justify-content: center; - align-items: center; - width: 20px; - height: 20px; - border: 1px solid var(--base500); - border-radius: 4px; -} - -.label { - margin-left: 10px; - user-select: none; /* disable text selection when clicking to toggle the checkbox */ -} - -.input { - position: absolute; - visibility: hidden; - height: 0; - width: 0; - bottom: 100%; - right: 100%; -} diff --git a/components/common/CopyButton.js b/components/common/CopyButton.js index b300ef31..610c1c1c 100644 --- a/components/common/CopyButton.js +++ b/components/common/CopyButton.js @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; -import Button from './Button'; +import { Button } from 'react-basics'; import { FormattedMessage } from 'react-intl'; const defaultText = ( diff --git a/components/common/DateFilter.js b/components/common/DateFilter.js index d568a889..1769a55c 100644 --- a/components/common/DateFilter.js +++ b/components/common/DateFilter.js @@ -1,14 +1,13 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; -import { endOfYear, isSameDay } from 'date-fns'; -import Modal from './Modal'; -import DropDown from './DropDown'; +import Calendar from 'assets/calendar-alt.svg'; import DatePickerForm from 'components/forms/DatePickerForm'; +import { endOfYear, isSameDay } from 'date-fns'; import useLocale from 'hooks/useLocale'; import { dateFormat } from 'lib/date'; -import Calendar from 'assets/calendar-alt.svg'; -import Icon from './Icon'; +import PropTypes from 'prop-types'; +import React, { useState } from 'react'; +import { Icon, Modal } from 'react-basics'; +import { FormattedMessage } from 'react-intl'; +import DropDown from './DropDown'; export const filterOptions = [ { label: , value: '1day' }, @@ -120,7 +119,9 @@ const CustomRange = ({ startDate, endDate, onClick }) => { return ( <> - } className="mr-2" onClick={handleClick} /> + + + {dateFormat(startDate, 'd LLL y', locale)} {!isSameDay(startDate, endDate) && ` — ${dateFormat(endDate, 'd LLL y', locale)}`} diff --git a/components/common/DropDown.js b/components/common/DropDown.js index 00d20e34..c84a9d04 100644 --- a/components/common/DropDown.js +++ b/components/common/DropDown.js @@ -5,7 +5,7 @@ import Menu from './Menu'; import useDocumentClick from 'hooks/useDocumentClick'; import Chevron from 'assets/chevron-down.svg'; import styles from './Dropdown.module.css'; -import Icon from './Icon'; +import { Icon } from 'react-basics'; function DropDown({ value, className, menuClassName, options = [], onChange = () => {} }) { const [showMenu, setShowMenu] = useState(false); @@ -33,7 +33,9 @@ function DropDown({ value, className, menuClassName, options = [], onChange = ()
{options.find(e => e.value === value)?.label || value}
- } className={styles.icon} size="small" /> + + +
{showMenu && ( - } size="xlarge" /> + + +

{msg}

- {children} + + {children} +
); } diff --git a/components/common/ErrorMessage.js b/components/common/ErrorMessage.js index 5747f226..efc6cc31 100644 --- a/components/common/ErrorMessage.js +++ b/components/common/ErrorMessage.js @@ -1,13 +1,15 @@ +import Exclamation from 'assets/exclamation-triangle.svg'; import React from 'react'; import { FormattedMessage } from 'react-intl'; -import Icon from './Icon'; -import Exclamation from 'assets/exclamation-triangle.svg'; import styles from './ErrorMessage.module.css'; +import { Icon } from 'react-basics'; export default function ErrorMessage() { return (
- } className={styles.icon} size="large" /> + + +
); diff --git a/components/common/EventDataButton.js b/components/common/EventDataButton.js index 2b895840..706d07d1 100644 --- a/components/common/EventDataButton.js +++ b/components/common/EventDataButton.js @@ -1,10 +1,9 @@ import List from 'assets/list-ul.svg'; -import Modal from 'components/common/Modal'; +import EventDataForm from 'components/forms/EventDataForm'; import PropTypes from 'prop-types'; import { useState } from 'react'; +import { Button, Icon, Modal } from 'react-basics'; import { FormattedMessage } from 'react-intl'; -import Button from './Button'; -import EventDataForm from 'components/forms/EventDataForm'; import styles from './EventDataButton.module.css'; function EventDataButton({ websiteId }) { @@ -23,13 +22,15 @@ function EventDataButton({ websiteId }) { return ( <> {showEventData && ( diff --git a/components/common/FilterButtons.js b/components/common/FilterButtons.js index ea811216..79962c51 100644 --- a/components/common/FilterButtons.js +++ b/components/common/FilterButtons.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import ButtonLayout from 'components/layout/ButtonLayout'; -import ButtonGroup from './ButtonGroup'; +import { ButtonGroup } from 'react-basics'; function FilterButtons({ buttons, selected, onClick }) { return ( diff --git a/components/common/FilterLink.js b/components/common/FilterLink.js index f16258f1..7500ffdb 100644 --- a/components/common/FilterLink.js +++ b/components/common/FilterLink.js @@ -4,7 +4,7 @@ import Link from 'next/link'; import { safeDecodeURI } from 'next-basics'; import usePageQuery from 'hooks/usePageQuery'; import External from 'assets/arrow-up-right-from-square.svg'; -import Icon from './Icon'; +import { Icon } from 'react-basics'; import styles from './FilterLink.module.css'; export default function FilterLink({ id, value, label, externalUrl }) { @@ -26,7 +26,9 @@ export default function FilterLink({ id, value, label, externalUrl }) { {externalUrl && ( - } className={styles.icon} /> + + + )} diff --git a/components/common/HamburgerButton.js b/components/common/HamburgerButton.js index 501b8c95..67a1c83a 100644 --- a/components/common/HamburgerButton.js +++ b/components/common/HamburgerButton.js @@ -1,4 +1,4 @@ -import Button from 'components/common/Button'; +import { Button, Icon } from 'react-basics'; import XMark from 'assets/xmark.svg'; import Bars from 'assets/bars.svg'; import { useState } from 'react'; @@ -33,11 +33,9 @@ export default function HamburgerButton() { return ( <> - {active && } ); diff --git a/components/common/Link.js b/components/common/Link.js index f683f5df..1e05d8b2 100644 --- a/components/common/Link.js +++ b/components/common/Link.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import NextLink from 'next/link'; -import Icon from './Icon'; +import { Icon } from 'react-basics'; import styles from './Link.module.css'; function Link({ className, icon, children, size, iconRight, onClick, ...props }) { diff --git a/components/common/MenuButton.js b/components/common/MenuButton.js index efe150f0..9eb4554f 100644 --- a/components/common/MenuButton.js +++ b/components/common/MenuButton.js @@ -2,12 +2,12 @@ import React, { useState, useRef } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import Menu from 'components/common/Menu'; -import Button from 'components/common/Button'; import useDocumentClick from 'hooks/useDocumentClick'; import styles from './MenuButton.module.css'; +import { Button } from 'react-basics'; function MenuButton({ - icon, + children, value, options, buttonClassName, @@ -41,7 +41,6 @@ function MenuButton({ return (
)} + {children} {showMenu && ( -
- {title &&
{title}
} -
{children}
-
- , - document.getElementById('__modals'), - ); -} - -Modal.propTypes = { - title: PropTypes.node, - children: PropTypes.node, -}; - -export default Modal; diff --git a/components/common/Modal.module.css b/components/common/Modal.module.css deleted file mode 100644 index 3bba9273..00000000 --- a/components/common/Modal.module.css +++ /dev/null @@ -1,46 +0,0 @@ -.modal { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - margin: auto; - z-index: 2; -} - -.modal:before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - margin: auto; - background: #000; - opacity: 0.5; -} - -.content { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: var(--base50); - min-width: 400px; - min-height: 100px; - max-width: 100vw; - z-index: 1; - border: 1px solid var(--base300); - padding: 30px; - border-radius: 4px; -} - -.header { - font-weight: 600; - margin-bottom: 20px; -} - -.body { - display: flex; - flex-direction: column; -} diff --git a/components/common/RefreshButton.js b/components/common/RefreshButton.js index be1a8a1d..89d8c9f2 100644 --- a/components/common/RefreshButton.js +++ b/components/common/RefreshButton.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import useStore from 'store/queries'; import { setDateRange } from 'store/websites'; -import Button from './Button'; +import { Button, Icon } from 'react-basics'; import Refresh from 'assets/redo.svg'; import Dots from 'assets/ellipsis-h.svg'; import useDateRange from 'hooks/useDateRange'; @@ -31,12 +31,13 @@ function RefreshButton({ websiteId }) { return ( ); } diff --git a/components/common/Table.js b/components/common/Table.js deleted file mode 100644 index 77a07712..00000000 --- a/components/common/Table.js +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import NoData from 'components/common/NoData'; -import styles from './Table.module.css'; - -function Table({ - columns, - rows, - empty, - className, - bodyClassName, - rowKey, - showHeader = true, - children, -}) { - if (empty && rows.length === 0) { - return empty; - } - - return ( -
- {showHeader && ( -
- {columns.map(({ key, label, className, style, header }) => ( -
- {label} -
- ))} -
- )} -
- {rows.length === 0 && } - {!children && - rows.map((row, index) => { - const id = rowKey ? rowKey(row) : index; - return ; - })} - {children} -
-
- ); -} - -const styledObject = PropTypes.shape({ - className: PropTypes.string, - style: PropTypes.object, -}); - -Table.propTypes = { - columns: PropTypes.arrayOf( - PropTypes.shape({ - cell: styledObject, - className: PropTypes.string, - header: styledObject, - key: PropTypes.string, - label: PropTypes.node, - render: PropTypes.func, - style: PropTypes.object, - }), - ), - rows: PropTypes.arrayOf(PropTypes.object), - empty: PropTypes.node, - className: PropTypes.string, - bodyClassName: PropTypes.string, - rowKey: PropTypes.func, - showHeader: PropTypes.bool, - children: PropTypes.node, -}; - -export default Table; - -export const TableRow = ({ columns, row }) => ( -
- {columns.map(({ key, label, render, className, style, cell }, index) => ( -
- {label && } - {render ? render(row) : row[key]} -
- ))} -
-); diff --git a/components/common/Table.module.css b/components/common/Table.module.css deleted file mode 100644 index ba4facc5..00000000 --- a/components/common/Table.module.css +++ /dev/null @@ -1,55 +0,0 @@ -.table { - display: flex; - flex-direction: column; -} - -.table label { - display: none; - font-size: var(--font-size-xs); - font-weight: bold; -} - -.header { - border-bottom: 1px solid var(--base300); -} - -.head { - font-size: var(--font-size-sm); - font-weight: 600; - line-height: 40px; -} - -.body { - position: relative; - display: flex; - flex-direction: column; -} - -.row { - border-bottom: 1px solid var(--base300); - padding: 10px 0; -} - -.cell { - display: flex; - flex-direction: column; - align-items: flex-start; -} - -@media only screen and (max-width: 992px) { - .table label { - display: block; - } - - .header { - display: none; - } - - .row { - flex-direction: column; - } - - .cell { - margin-bottom: 20px; - } -} diff --git a/components/common/Toast.js b/components/common/Toast.js deleted file mode 100644 index 1685eccc..00000000 --- a/components/common/Toast.js +++ /dev/null @@ -1,35 +0,0 @@ -import React, { useEffect } from 'react'; -import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; -import { useSpring, animated } from 'react-spring'; -import Icon from 'components/common/Icon'; -import Close from 'assets/times.svg'; -import styles from './Toast.module.css'; - -function Toast({ message, timeout = 3000, onClose }) { - const props = useSpring({ - opacity: 1, - transform: 'translate3d(0,0px,0)', - from: { opacity: 0, transform: 'translate3d(0,-40px,0)' }, - }); - - useEffect(() => { - setTimeout(onClose, timeout); - }, []); - - return ReactDOM.createPortal( - -
{message}
- } size="small" /> -
, - document.getElementById('__modals'), - ); -} - -Toast.propTypes = { - message: PropTypes.node, - timeout: PropTypes.number, - onClose: PropTypes.func, -}; - -export default Toast; diff --git a/components/common/Toast.module.css b/components/common/Toast.module.css deleted file mode 100644 index 18b8b44d..00000000 --- a/components/common/Toast.module.css +++ /dev/null @@ -1,25 +0,0 @@ -.toast { - position: fixed; - top: 30px; - left: 0; - right: 0; - width: 300px; - border-radius: 5px; - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 16px; - color: var(--msgColor); - background: var(--green400); - margin: auto; - z-index: 2; - cursor: pointer; -} - -.message { - font-size: var(--font-size-md); -} - -.close { - margin-left: 20px; -} diff --git a/components/common/UpdateNotice.js b/components/common/UpdateNotice.js index 54c5fb83..a98e3dca 100644 --- a/components/common/UpdateNotice.js +++ b/components/common/UpdateNotice.js @@ -4,7 +4,7 @@ import { setItem } from 'next-basics'; import ButtonLayout from 'components/layout/ButtonLayout'; import useStore, { checkVersion } from 'store/version'; import { REPO_URL, VERSION_CHECK } from 'lib/constants'; -import Button from './Button'; +import { Button } from 'react-basics'; import styles from './UpdateNotice.module.css'; export default function UpdateNotice() { diff --git a/components/forms/ChangePasswordForm.js b/components/forms/ChangePasswordForm.js deleted file mode 100644 index 8942ecf6..00000000 --- a/components/forms/ChangePasswordForm.js +++ /dev/null @@ -1,107 +0,0 @@ -import React, { useState } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Formik, Form, Field } from 'formik'; -import Button from 'components/common/Button'; -import FormLayout, { - FormButtons, - FormError, - FormMessage, - FormRow, -} from 'components/layout/FormLayout'; -import useApi from 'hooks/useApi'; -import useUser from 'hooks/useUser'; - -const initialValues = { - current_password: '', - new_password: '', - confirm_password: '', -}; - -const validate = ({ current_password, new_password, confirm_password }) => { - const errors = {}; - - if (!current_password) { - errors.current_password = ; - } - if (!new_password) { - errors.new_password = ; - } - if (!confirm_password) { - errors.confirm_password = ; - } else if (new_password !== confirm_password) { - errors.confirm_password = ( - - ); - } - - return errors; -}; - -export default function ChangePasswordForm({ values, onSave, onClose }) { - const { post } = useApi(); - const [message, setMessage] = useState(); - const { user } = useUser(); - - const handleSubmit = async values => { - const { ok, error } = await post(`/users/${user.id}/password`, values); - - if (ok) { - onSave(); - } else { - setMessage( - error || , - ); - } - }; - - return ( - - - {() => ( -
- - -
- - -
-
- - -
- - -
-
- - -
- - -
-
- - - - - {message} - - )} -
-
- ); -} diff --git a/components/forms/DatePickerForm.js b/components/forms/DatePickerForm.js index 9669f741..15f60a86 100644 --- a/components/forms/DatePickerForm.js +++ b/components/forms/DatePickerForm.js @@ -1,12 +1,11 @@ -import React, { useState } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { isAfter, isBefore, isSameDay } from 'date-fns'; import Calendar from 'components/common/Calendar'; -import Button from 'components/common/Button'; import { FormButtons } from 'components/layout/FormLayout'; +import { isAfter, isBefore, isSameDay } from 'date-fns'; import { getDateRangeValues } from 'lib/date'; +import React, { useState } from 'react'; +import { Button, ButtonGroup } from 'react-basics'; +import { FormattedMessage } from 'react-intl'; import styles from './DatePickerForm.module.css'; -import ButtonGroup from 'components/common/ButtonGroup'; const FILTER_DAY = 0; const FILTER_RANGE = 1; diff --git a/components/forms/DeleteForm.js b/components/forms/DeleteForm.js deleted file mode 100644 index 09da94d6..00000000 --- a/components/forms/DeleteForm.js +++ /dev/null @@ -1,105 +0,0 @@ -import React, { useState } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Formik, Form, Field } from 'formik'; -import Button from 'components/common/Button'; -import FormLayout, { - FormButtons, - FormError, - FormMessage, - FormRow, -} from 'components/layout/FormLayout'; -import Loading from 'components/common/Loading'; -import useApi from 'hooks/useApi'; - -const CONFIRMATION_WORD = 'DELETE'; - -const validate = ({ confirmation }) => { - const errors = {}; - - if (confirmation !== CONFIRMATION_WORD) { - errors.confirmation = !confirmation ? ( - - ) : ( - - ); - } - - return errors; -}; - -export default function DeleteForm({ values, onSave, onClose }) { - const { del } = useApi(); - const [message, setMessage] = useState(); - const [deleting, setDeleting] = useState(false); - - const handleSubmit = async ({ type, id }) => { - setDeleting(true); - - const { ok, data } = await del(`/${type}/${id}`); - - if (ok) { - onSave(); - } else { - setMessage( - data || , - ); - - setDeleting(false); - } - }; - - return ( - - {deleting && } - - {props => ( -
-
- {values.name} }} - /> -
-
- -
-

- {CONFIRMATION_WORD} }} - /> -

- -
- - -
-
- - - - - {message} - - )} -
-
- ); -} diff --git a/components/forms/EventDataForm.js b/components/forms/EventDataForm.js index e236aa3d..926e4a16 100644 --- a/components/forms/EventDataForm.js +++ b/components/forms/EventDataForm.js @@ -1,5 +1,5 @@ import classNames from 'classnames'; -import Button from 'components/common/Button'; +import { Button } from 'react-basics'; import DateFilter from 'components/common/DateFilter'; import DropDown from 'components/common/DropDown'; import FormLayout, { diff --git a/components/forms/ResetForm.js b/components/forms/ResetForm.js deleted file mode 100644 index 924aa7b1..00000000 --- a/components/forms/ResetForm.js +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useState } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Formik, Form, Field } from 'formik'; -import Button from 'components/common/Button'; -import FormLayout, { - FormButtons, - FormError, - FormMessage, - FormRow, -} from 'components/layout/FormLayout'; -import useApi from 'hooks/useApi'; - -const CONFIRMATION_WORD = 'RESET'; - -const validate = ({ confirmation }) => { - const errors = {}; - - if (confirmation !== CONFIRMATION_WORD) { - errors.confirmation = !confirmation ? ( - - ) : ( - - ); - } - - return errors; -}; - -export default function ResetForm({ values, onSave, onClose }) { - const { post } = useApi(); - const [message, setMessage] = useState(); - - const handleSubmit = async ({ type, id }) => { - const { ok, data } = await post(`/${type}/${id}/reset`); - - if (ok) { - onSave(); - } else { - setMessage( - data || , - ); - } - }; - - return ( - - - {props => ( -
-
- {values.name} }} - /> -
-
- -
-

- {CONFIRMATION_WORD} }} - /> -

- -
- - -
-
- - - - - {message} - - )} -
-
- ); -} diff --git a/components/forms/ShareUrlForm.js b/components/forms/ShareUrlForm.js index 9800e54b..d4eb5c8e 100644 --- a/components/forms/ShareUrlForm.js +++ b/components/forms/ShareUrlForm.js @@ -1,42 +1,85 @@ -import React, { useRef } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { useRouter } from 'next/router'; -import Button from 'components/common/Button'; -import FormLayout, { FormButtons, FormRow } from 'components/layout/FormLayout'; -import CopyButton from 'components/common/CopyButton'; +import { useMutation } from '@tanstack/react-query'; +import { getAuthToken } from 'lib/client'; +import { getRandomChars, useApi } from 'next-basics'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { + Button, + Form, + FormButtons, + FormRow, + HiddenInput, + SubmitButton, + TextField, + Toggle, +} from 'react-basics'; -export default function TrackingCodeForm({ values, onClose }) { - const ref = useRef(); - const { basePath } = useRouter(); - const { name, shareId } = values; +export default function ShareUrlForm({ websiteId, data, onSave }) { + const { name, shareId } = data; + const [id, setId] = useState(shareId); + const { post } = useApi(getAuthToken()); + const { mutate, error } = useMutation(({ shareId }) => + post(`/websites/${websiteId}`, { shareId }), + ); + const ref = useRef(null); + const url = useMemo( + () => `${process.env.analyticsUrl}/share/${id}/${encodeURIComponent(name)}`, + [id, name], + ); + + const generateId = () => getRandomChars(16); + + const handleSubmit = async data => { + mutate(data, { + onSuccess: async () => { + onSave(data); + ref.current.reset(data); + }, + }); + }; + + const handleGenerate = () => { + const id = generateId(); + ref.current.setValue('shareId', id, { + shouldValidate: true, + shouldDirty: true, + }); + setId(id); + }; + + const handleChange = checked => { + const data = { shareId: checked ? generateId() : null }; + mutate(data, { + onSuccess: async () => { + onSave(data); + }, + }); + setId(data.shareId); + }; + + useEffect(() => { + if (id && id !== shareId) { + ref.current.setValue('shareId', id); + } + }, [id, shareId]); return ( - -

- {values.name} }} - /> -

- -