From e2fcd40c2b9309df3564d36c8e5ca413ae412071 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 12 Apr 2023 13:40:19 -0700 Subject: [PATCH] Added SettingsTable. --- components/common/HamburgerButton.js | 14 ++ components/common/MobileMenu.js | 33 +++- components/common/MobileMenu.module.css | 10 + components/common/SettingsTable.js | 36 ++++ components/common/SettingsTable.module.css | 40 ++++ components/layout/NavBar.module.css | 4 + components/layout/PageHeader.js | 6 +- components/layout/PageHeader.module.css | 25 ++- components/layout/SettingsLayout.js | 8 +- components/layout/SettingsLayout.module.css | 6 +- components/layout/SideNav.module.css | 7 - components/pages/settings/teams/TeamsTable.js | 176 +++++++----------- components/pages/settings/users/UsersTable.js | 146 ++++++--------- .../pages/settings/websites/WebsitesTable.js | 96 +++------- .../websites/WebsitesTable.module.css | 13 ++ package.json | 4 +- pages/api/config.ts | 2 +- queries/admin/user.ts | 1 + yarn.lock | 8 +- 19 files changed, 335 insertions(+), 300 deletions(-) create mode 100644 components/common/SettingsTable.js create mode 100644 components/common/SettingsTable.module.css create mode 100644 components/pages/settings/websites/WebsitesTable.module.css diff --git a/components/common/HamburgerButton.js b/components/common/HamburgerButton.js index 77aaae75..9c92b282 100644 --- a/components/common/HamburgerButton.js +++ b/components/common/HamburgerButton.js @@ -19,6 +19,20 @@ export default function HamburgerButton() { !cloudMode && { label: formatMessage(labels.settings), value: '/settings', + children: [ + { + label: formatMessage(labels.websites), + value: '/settings/websites', + }, + { + label: formatMessage(labels.teams), + value: '/settings/teams', + }, + { + label: formatMessage(labels.users), + value: '/settings/users', + }, + ], }, { label: formatMessage(labels.profile), diff --git a/components/common/MobileMenu.js b/components/common/MobileMenu.js index aa737e7e..4168a6c6 100644 --- a/components/common/MobileMenu.js +++ b/components/common/MobileMenu.js @@ -1,17 +1,36 @@ import classNames from 'classnames'; +import { useRouter } from 'next/router'; import Link from 'next/link'; import styles from './MobileMenu.module.css'; export default function MobileMenu({ items = [], onClose }) { + const { pathname } = useRouter(); + + const Items = ({ items, className }) => ( +
+ {items.map(({ label, value, children }) => { + const selected = pathname === value; + + return ( + <> + + {label} + + {children && } + + ); + })} +
+ ); + return (
-
- {items.map(({ label, value }) => ( - - {label} - - ))} -
+
); } diff --git a/components/common/MobileMenu.module.css b/components/common/MobileMenu.module.css index aa9aafd0..cfe6cf37 100644 --- a/components/common/MobileMenu.module.css +++ b/components/common/MobileMenu.module.css @@ -25,5 +25,15 @@ } a.item { + color: var(--base600); +} + +a.item.selected, +.submenu a.item.selected { color: var(--base900); } + +.submenu a.item { + color: var(--base600); + margin-left: 40px; +} diff --git a/components/common/SettingsTable.js b/components/common/SettingsTable.js new file mode 100644 index 00000000..ac29d54e --- /dev/null +++ b/components/common/SettingsTable.js @@ -0,0 +1,36 @@ +import { Table, TableHeader, TableBody, TableRow, TableCell, TableColumn } from 'react-basics'; +import styles from './SettingsTable.module.css'; + +export default function SettingsTable({ columns = [], data = [], children, cellRender }) { + return ( + + + {(column, index) => { + return ( + + {column.label} + + ); + }} + + + {(row, keys, rowIndex) => { + row.action = children(row, keys, rowIndex); + + return ( + + {(data, key, colIndex) => { + return ( + + + {cellRender ? cellRender(row, data, key, colIndex) : data[key]} + + ); + }} + + ); + }} + +
+ ); +} diff --git a/components/common/SettingsTable.module.css b/components/common/SettingsTable.module.css new file mode 100644 index 00000000..023d1338 --- /dev/null +++ b/components/common/SettingsTable.module.css @@ -0,0 +1,40 @@ +.row .cell:last-child { + gap: 10px; + justify-content: flex-end; +} + +.label { + display: none; + font-weight: 700; +} + +@media screen and (max-width: 992px) { + .header .cell { + display: none; + } + + .label { + display: block; + min-width: 100px; + } + + .row .cell { + padding-left: 0; + flex-basis: 100%; + } +} + +@media screen and (max-width: 1200px) { + .row { + flex-wrap: wrap; + } + + .header .cell:last-child { + display: none; + } + + .row .cell:last-child { + padding-left: 0; + flex-basis: 100%; + } +} diff --git a/components/layout/NavBar.module.css b/components/layout/NavBar.module.css index 6c2faaaf..05dce2af 100644 --- a/components/layout/NavBar.module.css +++ b/components/layout/NavBar.module.css @@ -49,6 +49,10 @@ border-bottom: 2px solid transparent; } +.links span { + white-space: nowrap; +} + .links a:hover { color: var(--font-color100); border-bottom: 2px solid var(--primary400); diff --git a/components/layout/PageHeader.js b/components/layout/PageHeader.js index 0d014316..05c87f73 100644 --- a/components/layout/PageHeader.js +++ b/components/layout/PageHeader.js @@ -1,13 +1,9 @@ import React from 'react'; -import classNames from 'classnames'; -import { useBreakpoint } from 'react-basics'; import styles from './PageHeader.module.css'; export default function PageHeader({ title, children }) { - const breakPoint = useBreakpoint(); - return ( -
+
{title}
{children}
diff --git a/components/layout/PageHeader.module.css b/components/layout/PageHeader.module.css index d5481727..8a2a6800 100644 --- a/components/layout/PageHeader.module.css +++ b/components/layout/PageHeader.module.css @@ -16,6 +16,10 @@ color: var(--base900); } +.header > div { + line-height: 60px; +} + .title { display: flex; align-items: center; @@ -25,7 +29,22 @@ line-height: 50px; } -.xs .actions, -.sm .actions { - flex-basis: 100%; +.actions { + display: flex; + justify-content: flex-end; +} + +@media only screen and (max-width: 992px) { + .header { + margin-bottom: 10px; + } + + .title { + font-size: 18px; + } + + .actions { + flex-basis: 100%; + order: -1; + } } diff --git a/components/layout/SettingsLayout.js b/components/layout/SettingsLayout.js index 2b98aca5..ea0456e0 100644 --- a/components/layout/SettingsLayout.js +++ b/components/layout/SettingsLayout.js @@ -20,16 +20,16 @@ export default function SettingsLayout({ children }) { { key: 'profile', label: formatMessage(labels.profile), url: '/settings/profile' }, ].filter(n => n); - const getKey = () => items.find(({ url }) => pathname.startsWith(url))?.key; + const getKey = () => items.find(({ url }) => pathname === url)?.key; return ( - + {!cloudMode && ( - + )} - + {children} diff --git a/components/layout/SettingsLayout.module.css b/components/layout/SettingsLayout.module.css index 068681f5..569b903b 100644 --- a/components/layout/SettingsLayout.module.css +++ b/components/layout/SettingsLayout.module.css @@ -8,6 +8,8 @@ min-height: 50vh; } -.hideMenu .content { - margin: 0 auto; +@media only screen and (max-width: 768px) { + .menu { + display: none; + } } diff --git a/components/layout/SideNav.module.css b/components/layout/SideNav.module.css index 8b7d0d42..b664194d 100644 --- a/components/layout/SideNav.module.css +++ b/components/layout/SideNav.module.css @@ -13,10 +13,3 @@ padding: 0; border-radius: var(--border-radius); } - -@media only screen and (max-width: 768px) { - .menu { - flex-direction: row; - flex-wrap: wrap; - } -} diff --git a/components/pages/settings/teams/TeamsTable.js b/components/pages/settings/teams/TeamsTable.js index 1319c6d4..854b7bd9 100644 --- a/components/pages/settings/teams/TeamsTable.js +++ b/components/pages/settings/teams/TeamsTable.js @@ -1,126 +1,90 @@ import Link from 'next/link'; -import { - Button, - Flexbox, - Icon, - Icons, - Modal, - ModalTrigger, - Table, - TableBody, - TableCell, - TableColumn, - TableHeader, - TableRow, - Text, -} from 'react-basics'; +import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics'; import TeamDeleteForm from './TeamDeleteForm'; import TeamLeaveForm from './TeamLeaveForm'; import useMessages from 'hooks/useMessages'; import useUser from 'hooks/useUser'; import { ROLES } from 'lib/constants'; +import SettingsTable from 'components/common/SettingsTable'; export default function TeamsTable({ data = [], onDelete }) { const { formatMessage, labels } = useMessages(); const { user } = useUser(); const columns = [ - { name: 'name', label: formatMessage(labels.name), style: { flex: 2 } }, + { name: 'name', label: formatMessage(labels.name) }, { name: 'owner', label: formatMessage(labels.owner) }, { name: 'action', label: ' ' }, ]; + const cellRender = (row, data, key) => { + if (key === 'owner') { + return row.teamUser.find(({ role }) => role === ROLES.teamOwner)?.user?.username; + } + return data[key]; + }; + return ( - - - {(column, index) => { - return ( - - {column.label} - - ); - }} - - - {(row, keys, rowIndex) => { - const { id, teamUser } = row; - const owner = row.teamUser.find(({ role }) => role === ROLES.teamOwner); - const showDelete = user.id === owner?.userId; - const teamUserId = teamUser.find(a => a.userId === user.id).id; + + {row => { + const { id, teamUser } = row; + const owner = teamUser.find(({ role }) => role === ROLES.teamOwner); + const showDelete = user.id === owner?.userId; - const rowData = { - ...row, - owner: owner?.user?.username, - action: ( - - - - - {showDelete && ( - - - - {close => ( - - )} - - - )} - {!showDelete && ( - - - - {close => ( - - )} - - - )} - - ), - }; - - return ( - - {(data, key, colIndex) => { - return ( - - - {data[key]} - - - ); - }} - - ); - }} - -
+ return ( + <> + + + + {showDelete && ( + + + + {close => ( + + )} + + + )} + {!showDelete && ( + + + + {close => ( + + )} + + + )} + + ); + }} + ); } diff --git a/components/pages/settings/users/UsersTable.js b/components/pages/settings/users/UsersTable.js index 1542fe28..5ea3806c 100644 --- a/components/pages/settings/users/UsersTable.js +++ b/components/pages/settings/users/UsersTable.js @@ -1,109 +1,71 @@ -import { - Button, - Text, - Icon, - Table, - TableBody, - TableCell, - TableColumn, - TableHeader, - TableRow, - Flexbox, - Icons, - ModalTrigger, - Modal, -} from 'react-basics'; +import { Button, Text, Icon, Icons, ModalTrigger, Modal } from 'react-basics'; import { formatDistance } from 'date-fns'; import Link from 'next/link'; import useUser from 'hooks/useUser'; import UserDeleteForm from './UserDeleteForm'; import { ROLES } from 'lib/constants'; import useMessages from 'hooks/useMessages'; +import SettingsTable from 'components/common/SettingsTable'; export default function UsersTable({ data = [], onDelete }) { const { formatMessage, labels } = useMessages(); const { user } = useUser(); const columns = [ - { name: 'username', label: formatMessage(labels.username), style: { flex: 2 } }, - { name: 'role', label: formatMessage(labels.role), style: { flex: 1 } }, - { name: 'created', label: formatMessage(labels.created), style: { flex: 1 } }, - { name: 'action', label: ' ', style: { flex: 2 } }, + { name: 'username', label: formatMessage(labels.username), style: { flex: 1.5 } }, + { name: 'role', label: formatMessage(labels.role) }, + { name: 'created', label: formatMessage(labels.created) }, + { name: 'action', label: ' ' }, ]; - return ( - - - {(column, index) => { - return ( - - {column.label} - - ); - }} - - - {(row, keys, rowIndex) => { - const rowData = { - ...row, - created: formatDistance(new Date(row.createdAt), new Date(), { - addSuffix: true, - }), - role: formatMessage( - labels[Object.keys(ROLES).find(key => ROLES[key] === row.role)] || labels.unknown, - ), - action: ( - <> - - - - - - - {close => ( - - )} - - - - ), - }; + const cellRender = (row, data, key) => { + if (key === 'created') { + return formatDistance(new Date(row.createdAt), new Date(), { + addSuffix: true, + }); + } + if (key === 'role') { + return formatMessage( + labels[Object.keys(ROLES).find(key => ROLES[key] === row.role)] || labels.unknown, + ); + } + return data[key]; + }; - return ( - - {(data, key, colIndex) => { - return ( - - - {data[key]} - - - ); - }} - - ); - }} - -
+ return ( + + {(row, keys, rowIndex) => { + return ( + <> + + + + + + + {close => ( + + )} + + + + ); + }} + ); } diff --git a/components/pages/settings/websites/WebsitesTable.js b/components/pages/settings/websites/WebsitesTable.js index d658be48..0ed79e4f 100644 --- a/components/pages/settings/websites/WebsitesTable.js +++ b/components/pages/settings/websites/WebsitesTable.js @@ -1,83 +1,45 @@ import Link from 'next/link'; -import { - Table, - TableHeader, - TableBody, - TableRow, - TableCell, - TableColumn, - Button, - Text, - Icon, - Icons, - Flexbox, - useBreakpoint, -} from 'react-basics'; +import { Button, Text, Icon, Icons } from 'react-basics'; +import SettingsTable from 'components/common/SettingsTable'; import useMessages from 'hooks/useMessages'; import useConfig from 'hooks/useConfig'; export default function WebsitesTable({ data = [] }) { const { formatMessage, labels } = useMessages(); const { openExternal } = useConfig(); - const breakPoint = useBreakpoint(); const columns = [ - { name: 'name', label: formatMessage(labels.name), style: { flex: 2 } }, + { name: 'name', label: formatMessage(labels.name) }, { name: 'domain', label: formatMessage(labels.domain) }, - { name: 'action', label: ' ', style: { flexBasis: '100%' } }, + { name: 'action', label: ' ' }, ]; return ( - - - {(column, index) => { - return ( - - {column.label} - - ); - }} - - - {(row, keys, rowIndex) => { - const { id } = row; + + {row => { + const { id } = row; - row.action = ( - - - - - - - - - ); - - return ( - - {(data, key, colIndex) => { - return ( - - - {data[key]} - - - ); - }} - - ); - }} - -
+ return ( + <> + + + + + + + + ); + }} + ); } diff --git a/components/pages/settings/websites/WebsitesTable.module.css b/components/pages/settings/websites/WebsitesTable.module.css new file mode 100644 index 00000000..a26c349f --- /dev/null +++ b/components/pages/settings/websites/WebsitesTable.module.css @@ -0,0 +1,13 @@ +@media screen and (max-width: 992px) { + .row { + flex-wrap: wrap; + } + + .header .actions { + display: none; + } + + .actions { + flex-basis: 100%; + } +} diff --git a/package.json b/package.json index b63935a2..c699d1f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "2.0.0-beta.4", + "version": "2.0.0-beta.5", "description": "A simple, fast, privacy-focused alternative to Google Analytics.", "author": "Mike Cao ", "license": "MIT", @@ -94,7 +94,7 @@ "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", "react": "^18.2.0", - "react-basics": "^0.75.0", + "react-basics": "^0.76.0", "react-beautiful-dnd": "^13.1.0", "react-dom": "^18.2.0", "react-intl": "^5.24.7", diff --git a/pages/api/config.ts b/pages/api/config.ts index bccfd048..a8ae1cb0 100644 --- a/pages/api/config.ts +++ b/pages/api/config.ts @@ -16,7 +16,7 @@ export default async (req: NextApiRequest, res: NextApiResponse) trackerScriptName: process.env.TRACKER_SCRIPT_NAME, updatesDisabled: !!process.env.DISABLE_UPDATES, telemetryDisabled: !!process.env.DISABLE_TELEMETRY, - cloudMode: !!process.env.CLOUD_MODE, + cloudMode: false, //!!process.env.CLOUD_MODE, }); } diff --git a/queries/admin/user.ts b/queries/admin/user.ts index 431e934b..1f3a2bd0 100644 --- a/queries/admin/user.ts +++ b/queries/admin/user.ts @@ -23,6 +23,7 @@ export async function getUser( export async function getUsers(): Promise { return prisma.client.user.findMany({ + take: 100, where: { deletedAt: null, }, diff --git a/yarn.lock b/yarn.lock index b759ff50..10c18dc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6727,10 +6727,10 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-basics@^0.75.0: - version "0.75.0" - resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.75.0.tgz#501ba7fae6e0659ec8f7e811a4303ef83bd57b13" - integrity sha512-mDm+L/cw4LX4LylJW0MyV+YFxhZ0tMa/bCIs1QsApcRGvF4ahlB1rdwWn6/p01PVSHe7xS/55lSklp3b7IWOaw== +react-basics@^0.76.0: + version "0.76.0" + resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.76.0.tgz#7369ba68409388f458a2ecf73a86603884fc0711" + integrity sha512-RRtudldMecbuT/ap1giy6OdNc1t8gfGdyfXDTy4x99PWN9kvfS8MU11cfyQif8F0C6v9wKFu2taxklQQarE+mw== dependencies: classnames "^2.3.1" date-fns "^2.29.3"