diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..cf134e99 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "eslint.workingDirectories": [ + "./apps/ui", + "./apps/platform" + ] +} \ No newline at end of file diff --git a/apps/ui/package.json b/apps/ui/package.json index ad585f89..bc36e822 100644 --- a/apps/ui/package.json +++ b/apps/ui/package.json @@ -19,7 +19,6 @@ "@types/uuid": "^9.0.0", "autoprefixer": "^10.4.12", "axios": "0.27.2", - "bootstrap-icons": "1.10.3", "clsx": "^1.2.1", "date-fns": "^2.29.3", "date-fns-tz": "^1.3.7", diff --git a/apps/ui/src/ui/Button.css b/apps/ui/src/ui/Button.css index e08002d4..2e051b31 100644 --- a/apps/ui/src/ui/Button.css +++ b/apps/ui/src/ui/Button.css @@ -1,5 +1,5 @@ .ui-button { - display: inline-block; + display: inline-flex; border: 1px solid transparent; background: transparent; border-radius: var(--border-radius); @@ -14,14 +14,21 @@ transition: .2s; text-decoration: none; position: relative; + align-items: center; + gap: 5px; } -.ui-button i { - margin-right: 5px; - margin-left: -1px; +.ui-button .button-icon, +.ui-button .button-icon svg { + width: 16px; + height: 16px; } -.ui-button.ui-button-no-children i { +.ui-button .button-icon { + margin-left: -2px; +} + +.ui-button.ui-button-no-children .button-icon { margin: 0; } diff --git a/apps/ui/src/ui/Button.tsx b/apps/ui/src/ui/Button.tsx index 1cc4483e..fcfbd514 100644 --- a/apps/ui/src/ui/Button.tsx +++ b/apps/ui/src/ui/Button.tsx @@ -10,7 +10,7 @@ type BaseButtonProps = PropsWithChildren<{ children?: React.ReactNode variant?: ButtonVariant size?: ButtonSize - icon?: string + icon?: React.ReactNode isLoading?: boolean }> & JSX.IntrinsicElements['button'] @@ -28,7 +28,7 @@ const LinkButton = forwardRef(function LinkButton(props: LinkButtonProps, ref: R - {props.icon != null && } + {props.icon && ({props.icon})} {props.children} ) @@ -61,7 +61,7 @@ const Button = forwardRef(function Button(props: ButtonProps, ref: Ref - {icon != null && } + {icon && ({icon})} {children} ) diff --git a/apps/ui/src/ui/Menu.css b/apps/ui/src/ui/Menu.css index 1c8231c6..6b4f34da 100644 --- a/apps/ui/src/ui/Menu.css +++ b/apps/ui/src/ui/Menu.css @@ -5,12 +5,13 @@ box-shadow: rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.1) 0px 4px 6px -4px; z-index: 999; min-width: 150px; - padding: 5px 0; + padding: 3px 0; + font-size: 13px; + overflow: hidden; } .ui-menu-item { - padding: 10px; - border-bottom: 1px solid var(--color-grey); + padding: 7px 10px; display: flex; gap: 5px; align-items: center; @@ -25,7 +26,8 @@ } .ui-menu-item svg { - width: 16px; - height: 16px; + width: 14px; + height: 14px; margin-top: -1px; + margin-right: 5px; } \ No newline at end of file diff --git a/apps/ui/src/ui/Menu.tsx b/apps/ui/src/ui/Menu.tsx index fcad53db..32231a20 100644 --- a/apps/ui/src/ui/Menu.tsx +++ b/apps/ui/src/ui/Menu.tsx @@ -4,6 +4,7 @@ import { usePopper } from 'react-popper' import Button, { ButtonSize, ButtonVariant } from './Button' import './Menu.css' import { Placement } from '@popperjs/core' +import { ThreeDotsIcon } from './icons' interface MenuProps { thing?: string @@ -40,7 +41,7 @@ export default function Menu({ children, variant, size, placement }: PropsWithCh ref={setReferenceElement} variant={variant ?? 'plain'} size={size} - icon="three-dots"> + icon={}> onClose(false)} - icon="x-lg" + icon={} > {'Exit'} diff --git a/apps/ui/src/ui/NavLink.tsx b/apps/ui/src/ui/NavLink.tsx index 4bbc8680..0323316e 100644 --- a/apps/ui/src/ui/NavLink.tsx +++ b/apps/ui/src/ui/NavLink.tsx @@ -1,10 +1,10 @@ import * as React from 'react' import { NavLink as BaseNavLink, NavLinkProps as BaseNavLinkProps } from 'react-router-dom' -export type NavLinkProps = BaseNavLinkProps & { key: string, icon?: string } +export type NavLinkProps = BaseNavLinkProps & { key: string, icon?: React.ReactNode } const NavLink = React.forwardRef( - function NavLink({ ...props }: NavLinkProps, ref: React.Ref | undefined) { + function NavLink({ icon, ...props }: NavLinkProps, ref: React.Ref | undefined) { return ( <> - {props.icon && } + {icon && (
{icon}
)}   {props.children} diff --git a/apps/ui/src/ui/Pagination.css b/apps/ui/src/ui/Pagination.css index 88772dda..5ad2b9ca 100644 --- a/apps/ui/src/ui/Pagination.css +++ b/apps/ui/src/ui/Pagination.css @@ -17,6 +17,7 @@ .ui-pagination .pagination-button { background: var(--color-background); font-size: 16px; + line-height: 15px; cursor: pointer; text-decoration: none; color: var(--color-primary); @@ -29,7 +30,7 @@ .ui-pagination .pagination-button:disabled { color: var(--color-primary-soft); - cursor: auto; + cursor: not-allowed; } .ui-pagination .pagination-button:disabled:hover { @@ -41,13 +42,22 @@ font-weight: 500; } -.ui-pagination .prev i { - margin-right: 5px; +.ui-pagination .prev, +.ui-pagination .next { + display: flex; + align-items: center; + gap: 5px; +} + +.ui-pagination .prev svg, +.ui-pagination .next svg { + width: 16px; } .ui-pagination .next { border-right-width: 0px; } -.ui-pagination .next i { - margin-left: 5px; + +.ui-pagination .next svg { + width: 16px; } diff --git a/apps/ui/src/ui/Pagination.tsx b/apps/ui/src/ui/Pagination.tsx index 97a23e54..5c9c2be3 100644 --- a/apps/ui/src/ui/Pagination.tsx +++ b/apps/ui/src/ui/Pagination.tsx @@ -1,3 +1,4 @@ +import { ChevronLeftIcon, ChevronRightIcon } from './icons' import './Pagination.css' interface PaginationProps { @@ -81,7 +82,7 @@ export default function Pagination({ { @@ -95,7 +96,7 @@ export default function Pagination({ disabled={(page + 1) >= total} onClick={() => onChangePage(page + 1)}> Next - + ) diff --git a/apps/ui/src/ui/Sidebar.css b/apps/ui/src/ui/Sidebar.css index e8557f79..6578de09 100644 --- a/apps/ui/src/ui/Sidebar.css +++ b/apps/ui/src/ui/Sidebar.css @@ -59,7 +59,7 @@ nav { } nav a { - display: block; + display: flex; padding: 15px; text-decoration: none; color: var(--color-primary); @@ -67,6 +67,8 @@ nav a { font-size: 15px; font-weight: 500; margin-bottom: 5px; + align-items: center; + gap: 5px; } nav a:hover { @@ -79,8 +81,10 @@ nav a.selected { background: var(--color-primary); } -nav a i { - margin-right: 5px; +nav a .nav-icon { + width: 16px; + height: 16px; + display: flex; } .sidebar-profile { diff --git a/apps/ui/src/ui/Sidebar.tsx b/apps/ui/src/ui/Sidebar.tsx index 44546db9..9b41b2c3 100644 --- a/apps/ui/src/ui/Sidebar.tsx +++ b/apps/ui/src/ui/Sidebar.tsx @@ -2,7 +2,7 @@ import './Sidebar.css' import NavLink from './NavLink' import { ReactComponent as Logo } from '../assets/logo.svg' import { Link, NavLinkProps, useNavigate } from 'react-router-dom' -import { PropsWithChildren, useContext } from 'react' +import { PropsWithChildren, ReactNode, useContext } from 'react' import { AdminContext, ProjectContext } from '../contexts' import api from '../api' import { PreferencesContext } from './PreferencesContext' @@ -10,9 +10,10 @@ import { useResolver } from '../hooks' import { SingleSelect } from './form/SingleSelect' import Button, { LinkButton } from './Button' import ButtonGroup from './ButtonGroup' +import { MoonIcon, PlusIcon, SunIcon } from './icons' interface SidebarProps { - links?: Array + links?: Array } export default function Sidebar({ children, links }: PropsWithChildren) { @@ -51,7 +52,7 @@ export default function Sidebar({ children, links }: PropsWithChildren - + }> {'Create Project'} @@ -74,7 +75,7 @@ export default function Sidebar({ children, links }: PropsWithChildren : } onClick={() => setPreferences({ ...preferences, mode: preferences.mode === 'dark' ? 'light' : 'dark' })} /> , + aborted: , + scheduled: , + running: , finished: <>, } diff --git a/apps/ui/src/views/campaign/Campaigns.tsx b/apps/ui/src/views/campaign/Campaigns.tsx index 39b8ddb9..7c5a5be4 100644 --- a/apps/ui/src/views/campaign/Campaigns.tsx +++ b/apps/ui/src/views/campaign/Campaigns.tsx @@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router' import api from '../../api' import { CampaignState } from '../../types' import Button from '../../ui/Button' -import { ArchiveIcon, DuplicateIcon, EditIcon } from '../../ui/icons' +import { ArchiveIcon, DuplicateIcon, EditIcon, PlusIcon } from '../../ui/icons' import Menu, { MenuItem } from '../../ui/Menu' import PageContent from '../../ui/PageContent' import { SearchTable, useSearchTableQueryState } from '../../ui/SearchTable' @@ -49,7 +49,7 @@ export default function Campaigns() { return ( <> setIsCreateOpen(true)}>Create Campaign + }> ( handleEditCampaign(id)}> - Edit + Edit await handleDuplicateCampaign(id)}> - Duplicate + Duplicate await handleArchiveCampaign(id)}> - Archive + Archive ), diff --git a/apps/ui/src/views/campaign/EmailEditor.tsx b/apps/ui/src/views/campaign/EmailEditor.tsx index c8c3e29e..85da9e84 100644 --- a/apps/ui/src/views/campaign/EmailEditor.tsx +++ b/apps/ui/src/views/campaign/EmailEditor.tsx @@ -15,6 +15,7 @@ import { PreferencesContext } from '../../ui/PreferencesContext' import CreateLocaleModal from './CreateLocaleModal' import ImageGalleryModal from './ImageGalleryModal' import Modal from '../../ui/Modal' +import { ImageIcon } from '../../ui/icons' const HtmlEditor = ({ template, setTemplate }: { template: Template, setTemplate: (template: Template) => void }) => { @@ -89,7 +90,7 @@ const HtmlEditor = ({ template, setTemplate }: { template: Template, setTemplate
diff --git a/apps/ui/src/views/journey/Journeys.tsx b/apps/ui/src/views/journey/Journeys.tsx index d8a27dc8..3f563951 100644 --- a/apps/ui/src/views/journey/Journeys.tsx +++ b/apps/ui/src/views/journey/Journeys.tsx @@ -8,6 +8,7 @@ import FormWrapper from '../../ui/form/FormWrapper' import Modal from '../../ui/Modal' import PageContent from '../../ui/PageContent' import { SearchTable, useSearchTableQueryState } from '../../ui/SearchTable' +import { PlusIcon } from '../../ui/icons' import { TagPicker } from '../settings/TagPicker' export default function Journeys() { @@ -20,7 +21,7 @@ export default function Journeys() { setOpen('create')}> + } diff --git a/apps/ui/src/views/router.tsx b/apps/ui/src/views/router.tsx index 0fc3496d..74afa895 100644 --- a/apps/ui/src/views/router.tsx +++ b/apps/ui/src/views/router.tsx @@ -36,6 +36,7 @@ import Login from './auth/Login' import OnboardingStart from './auth/OnboardingStart' import Onboarding from './auth/Onboarding' import OnboardingProject from './auth/OnboardingProject' +import { CampaignsIcon, JourneysIcon, ListsIcon, SettingsIcon, UsersIcon } from '../ui/icons' export const useRoute = (includeProject = true) => { const { projectId = '' } = useParams() @@ -116,31 +117,31 @@ export const router = createBrowserRouter([ key: 'campaigns', to: 'campaigns', children: 'Campaigns', - icon: 'bi-megaphone', + icon: , }, { key: 'journeys', to: 'journeys', children: 'Journeys', - icon: 'bi-diagram-2', + icon: , }, { key: 'users', to: 'users', children: 'Users', - icon: 'bi-people', + icon: , }, { key: 'lists', to: 'lists', children: 'Lists', - icon: 'bi-list-ol', + icon: , }, { key: 'settings', to: 'settings', children: 'Settings', - icon: 'bi-gear', + icon: , }, ]} > diff --git a/apps/ui/src/views/settings/ApiKeys.tsx b/apps/ui/src/views/settings/ApiKeys.tsx index 8a8d1672..067c4fa0 100644 --- a/apps/ui/src/views/settings/ApiKeys.tsx +++ b/apps/ui/src/views/settings/ApiKeys.tsx @@ -9,6 +9,7 @@ import TextField from '../../ui/form/TextField' import FormWrapper from '../../ui/form/FormWrapper' import Modal from '../../ui/Modal' import { SearchTable, useSearchTableState } from '../../ui/SearchTable' +import { PlusIcon } from '../../ui/icons' export default function ProjectApiKeys() { @@ -32,7 +33,7 @@ export default function ProjectApiKeys() { onSelectRow={(row: ProjectApiKey) => navigate(`${row.id}`)} title="API Keys" actions={ - } diff --git a/apps/ui/src/views/settings/IntegrationModal.tsx b/apps/ui/src/views/settings/IntegrationModal.tsx index 2e4dd240..521cacc0 100644 --- a/apps/ui/src/views/settings/IntegrationModal.tsx +++ b/apps/ui/src/views/settings/IntegrationModal.tsx @@ -12,6 +12,7 @@ import Modal, { ModalProps } from '../../ui/Modal' import Tile, { TileGrid } from '../../ui/Tile' import { snakeToTitle } from '../../utils' import './IntegrationModal.css' +import { ChevronLeftIcon } from '../../ui/icons' interface IntegrationModalProps extends Omit { provider: Provider | undefined @@ -61,7 +62,7 @@ export default function IntegrationModal({ onChange, provider, ...props }: Integ {meta && <> {!provider?.id &&
diff --git a/apps/ui/src/views/settings/Integrations.tsx b/apps/ui/src/views/settings/Integrations.tsx index 7cbc7a9e..701421cf 100644 --- a/apps/ui/src/views/settings/Integrations.tsx +++ b/apps/ui/src/views/settings/Integrations.tsx @@ -4,6 +4,7 @@ import { ProjectContext } from '../../contexts' import { Provider } from '../../types' import Button from '../../ui/Button' import Heading from '../../ui/Heading' +import { PlusIcon } from '../../ui/icons' import { SearchTable, useSearchTableState } from '../../ui/SearchTable' import IntegrationModal from './IntegrationModal' @@ -16,7 +17,7 @@ export default function Integrations() { return ( <> { + diff --git a/apps/ui/src/views/settings/Subscriptions.tsx b/apps/ui/src/views/settings/Subscriptions.tsx index ad978356..a9db4352 100644 --- a/apps/ui/src/views/settings/Subscriptions.tsx +++ b/apps/ui/src/views/settings/Subscriptions.tsx @@ -9,6 +9,7 @@ import { Subscription } from '../../types' import TextField from '../../ui/form/TextField' import { SingleSelect } from '../../ui/form/SingleSelect' import Button from '../../ui/Button' +import { PlusIcon } from '../../ui/icons' export default function Subscriptions() { const navigate = useNavigate() @@ -31,11 +32,11 @@ export default function Subscriptions() { <> } diff --git a/apps/ui/src/views/settings/Tags.tsx b/apps/ui/src/views/settings/Tags.tsx index ee8607f8..fbe5ca52 100644 --- a/apps/ui/src/views/settings/Tags.tsx +++ b/apps/ui/src/views/settings/Tags.tsx @@ -5,6 +5,7 @@ import { Tag } from '../../types' import Button from '../../ui/Button' import FormWrapper from '../../ui/form/FormWrapper' import TextField from '../../ui/form/TextField' +import { PlusIcon } from '../../ui/icons' import Modal from '../../ui/Modal' import { SearchTable, useSearchTableState } from '../../ui/SearchTable' @@ -36,7 +37,7 @@ export default function Tags() { size="small" variant="primary" onClick={() => setEditing({ id: 0, name: 'New Tag' })} - icon="plus" + icon={} > {'Create Tag'} diff --git a/apps/ui/src/views/settings/Teams.tsx b/apps/ui/src/views/settings/Teams.tsx index 04a5c61c..b1c8097f 100644 --- a/apps/ui/src/views/settings/Teams.tsx +++ b/apps/ui/src/views/settings/Teams.tsx @@ -7,7 +7,7 @@ import Button from '../../ui/Button' import { DataTableCol } from '../../ui/DataTable' import { SelectionProps } from '../../ui/form/Field' import FormWrapper from '../../ui/form/FormWrapper' -import { ArchiveIcon } from '../../ui/icons' +import { ArchiveIcon, PlusIcon } from '../../ui/icons' import Menu, { MenuItem } from '../../ui/Menu' import Modal from '../../ui/Modal' import { SearchTable, SearchTableQueryState, useSearchTableState } from '../../ui/SearchTable' @@ -92,7 +92,7 @@ export default function Teams() { title="Team" onDeleteRow={handleDeleteProjectAdmin} actions={ - } diff --git a/apps/ui/src/views/users/ListDetail.tsx b/apps/ui/src/views/users/ListDetail.tsx index a24c0e8c..bc649e5d 100644 --- a/apps/ui/src/views/users/ListDetail.tsx +++ b/apps/ui/src/views/users/ListDetail.tsx @@ -16,6 +16,7 @@ import { snakeToTitle } from '../../utils' import UploadField from '../../ui/form/UploadField' import { SearchTable, useSearchTableState } from '../../ui/SearchTable' import { useRoute } from '../router' +import { EditIcon, UploadIcon } from '../../ui/icons' import { TagPicker } from '../settings/TagPicker' const RuleSection = ({ list, onRuleSave }: { list: DynamicList, onRuleSave: (rule: WrapperRule) => void }) => { @@ -64,10 +65,10 @@ export default function ListDetail() { <> {list.type === 'static' && } - + }> diff --git a/apps/ui/src/views/users/Lists.tsx b/apps/ui/src/views/users/Lists.tsx index 7c5a84bb..478ac3d1 100644 --- a/apps/ui/src/views/users/Lists.tsx +++ b/apps/ui/src/views/users/Lists.tsx @@ -10,6 +10,7 @@ import Modal from '../../ui/Modal' import PageContent from '../../ui/PageContent' import ListTable from './ListTable' import { createWrapperRule } from './RuleBuilder' +import { PlusIcon } from '../../ui/icons' import { TagPicker } from '../settings/TagPicker' export default function Lists() { @@ -24,7 +25,7 @@ export default function Lists() { title="Lists" actions={
} @@ -130,20 +131,20 @@ const RuleSet = ({ group, onChange, onDelete }: RuleSetParams) => { ? : <> @@ -209,7 +210,7 @@ const RuleView = ({ rule, onChange, onDelete }: RuleParams) => { />