diff --git a/package.json b/package.json
index 367627e6..f9b4029c 100644
--- a/package.json
+++ b/package.json
@@ -92,6 +92,7 @@
"isbot": "^3.4.5",
"kafkajs": "^2.1.0",
"maxmind": "^4.3.6",
+ "md5": "^2.3.0",
"moment-timezone": "^0.5.35",
"next": "14.0.4",
"next-basics": "^0.39.0",
diff --git a/src/app/(main)/NavBar.tsx b/src/app/(main)/NavBar.tsx
index 8bdee61a..fdf70ae8 100644
--- a/src/app/(main)/NavBar.tsx
+++ b/src/app/(main)/NavBar.tsx
@@ -2,18 +2,20 @@
import { Icon, Text } from 'react-basics';
import Link from 'next/link';
import classNames from 'classnames';
-import Icons from 'components/icons';
+import HamburgerButton from 'components/common/HamburgerButton';
import ThemeButton from 'components/input/ThemeButton';
import LanguageButton from 'components/input/LanguageButton';
import ProfileButton from 'components/input/ProfileButton';
-import { useMessages, useNavigation } from 'components/hooks';
-import HamburgerButton from 'components/common/HamburgerButton';
+import TeamsButton from 'components/input/TeamsButton';
+import Icons from 'components/icons';
+import { useLogin, useMessages, useNavigation } from 'components/hooks';
import styles from './NavBar.module.css';
export function NavBar() {
+ const { user } = useLogin();
const { formatMessage, labels } = useMessages();
const cloudMode = Boolean(process.env.cloudMode);
- const { pathname, renderTeamUrl } = useNavigation();
+ const { pathname, renderTeamUrl, teamId } = useNavigation();
const links = [
{ label: formatMessage(labels.dashboard), url: renderTeamUrl('/dashboard') },
@@ -79,6 +81,7 @@ export function NavBar() {
})}
+ {user?.teams?.length &&
}
diff --git a/src/components/common/Avatar.tsx b/src/components/common/Avatar.tsx
new file mode 100644
index 00000000..b25b0854
--- /dev/null
+++ b/src/components/common/Avatar.tsx
@@ -0,0 +1,158 @@
+import md5 from 'md5';
+
+const pallette = [
+ ['#ff9a9e', '#fad0c4'],
+ ['#a18cd1', '#fbc2eb'],
+ ['#fad0c4', '#ffd1ff'],
+ ['#ffecd2', '#fcb69f'],
+ ['#ff9a9e', '#fecfef'],
+ ['#f6d365', '#fda085'],
+ ['#fbc2eb', '#a6c1ee'],
+ ['#fdcbf1', '#e6dee9'],
+ ['#a1c4fd', '#c2e9fb'],
+ ['#d4fc79', '#96e6a1'],
+ ['#84fab0', '#8fd3f4'],
+ ['#cfd9df', '#e2ebf0'],
+ ['#a6c0fe', '#f68084'],
+ ['#fccb90', '#d57eeb'],
+ ['#e0c3fc', '#8ec5fc'],
+ ['#f093fb', '#f5576c'],
+ ['#fdfbfb', '#ebedee'],
+ ['#4facfe', '#00f2fe'],
+ ['#43e97b', '#38f9d7'],
+ ['#fa709a', '#fee140'],
+ ['#30cfd0', '#330867'],
+ ['#a8edea', '#fed6e3'],
+ ['#5ee7df', '#b490ca'],
+ ['#d299c2', '#fef9d7'],
+ ['#f5f7fa', '#c3cfe2'],
+ ['#667eea', '#764ba2'],
+ ['#fdfcfb', '#e2d1c3'],
+ ['#89f7fe', '#66a6ff'],
+ ['#fddb92', '#d1fdff'],
+ ['#9890e3', '#b1f4cf'],
+ ['#ebc0fd', '#d9ded8'],
+ ['#96fbc4', '#f9f586'],
+ ['#2af598', '#009efd'],
+ ['#cd9cf2', '#f6f3ff'],
+ ['#6a11cb', '#2575fc'],
+ ['#37ecba', '#72afd3'],
+ ['#ebbba7', '#cfc7f8'],
+ ['#fff1eb', '#ace0f9'],
+ ['#c471f5', '#fa71cd'],
+ ['#48c6ef', '#6f86d6'],
+ ['#feada6', '#f5efef'],
+ ['#e6e9f0', '#eef1f5'],
+ ['#accbee', '#e7f0fd'],
+ ['#e9defa', '#fbfcdb'],
+ ['#c1dfc4', '#deecdd'],
+ ['#0ba360', '#3cba92'],
+ ['#00c6fb', '#005bea'],
+ ['#74ebd5', '#9face6'],
+ ['#6a85b6', '#bac8e0'],
+ ['#a3bded', '#6991c7'],
+ ['#9795f0', '#fbc8d4'],
+ ['#a7a6cb', '#8989ba'],
+ ['#f43b47', '#453a94'],
+ ['#0250c5', '#d43f8d'],
+ ['#88d3ce', '#6e45e2'],
+ ['#d9afd9', '#97d9e1'],
+ ['#7028e4', '#e5b2ca'],
+ ['#13547a', '#80d0c7'],
+ ['#ff0844', '#ffb199'],
+ ['#93a5cf', '#e4efe9'],
+ ['#434343', '#000000'],
+ ['#93a5cf', '#e4efe9'],
+ ['#92fe9d', '#00c9ff'],
+ ['#ff758c', '#ff7eb3'],
+ ['#868f96', '#596164'],
+ ['#c79081', '#dfa579'],
+ ['#8baaaa', '#ae8b9c'],
+ ['#f83600', '#f9d423'],
+ ['#b721ff', '#21d4fd'],
+ ['#6e45e2', '#88d3ce'],
+ ['#d558c8', '#24d292'],
+ ['#abecd6', '#fbed96'],
+ ['#5f72bd', '#9b23ea'],
+ ['#09203f', '#537895'],
+ ['#ddd6f3', '#faaca8'],
+ ['#dcb0ed', '#99c99c'],
+ ['#f3e7e9', '#e3eeff'],
+ ['#c71d6f', '#d09693'],
+ ['#96deda', '#50c9c3'],
+ ['#f77062', '#fe5196'],
+ ['#a8caba', '#5d4157'],
+ ['#29323c', '#485563'],
+ ['#16a085', '#f4d03f'],
+ ['#ff5858', '#f09819'],
+ ['#2b5876', '#4e4376'],
+ ['#00cdac', '#8ddad5'],
+ ['#4481eb', '#04befe'],
+ ['#dad4ec', '#f3e7e9'],
+ ['#874da2', '#c43a30'],
+ ['#4481eb', '#04befe'],
+ ['#e8198b', '#c7eafd'],
+ ['#f794a4', '#fdd6bd'],
+ ['#64b3f4', '#c2e59c'],
+ ['#0fd850', '#f9f047'],
+ ['#ee9ca7', '#ffdde1'],
+ ['#209cff', '#68e0cf'],
+ ['#bdc2e8', '#e6dee9'],
+ ['#e6b980', '#eacda3'],
+ ['#1e3c72', '#2a5298'],
+ ['#9be15d', '#00e3ae'],
+ ['#ed6ea0', '#ec8c69'],
+ ['#ffc3a0', '#ffafbd'],
+ ['#cc208e', '#6713d2'],
+ ['#b3ffab', '#12fff7'],
+ ['#243949', '#517fa4'],
+ ['#fc6076', '#ff9a44'],
+ ['#dfe9f3', '#ffffff'],
+ ['#00dbde', '#fc00ff'],
+ ['#f9d423', '#ff4e50'],
+ ['#50cc7f', '#f5d100'],
+ ['#0acffe', '#495aff'],
+ ['#616161', '#9bc5c3'],
+ ['#df89b5', '#bfd9fe'],
+ ['#ed6ea0', '#ec8c69'],
+ ['#d7d2cc', '#304352'],
+ ['#e14fad', '#f9d423'],
+ ['#b224ef', '#7579ff'],
+ ['#c1c161', '#d4d4b1'],
+ ['#ec77ab', '#7873f5'],
+ ['#007adf', '#00ecbc'],
+ ['#20E2D7', '#F9FEA5'],
+ ['#A8BFFF', '#884D80'],
+ ['#B6CEE8', '#F578DC'],
+ ['#FFFEFF', '#D7FFFE'],
+ ['#E3FDF5', '#FFE6FA'],
+ ['#7DE2FC', '#B9B6E5'],
+ ['#CBBACC', '#2580B3'],
+ ['#B7F8DB', '#50A7C2'],
+ ['#007adf', '#00ecbc'],
+];
+
+export function Avatar({ value }: { value: string }) {
+ const hash = md5(value);
+ let num = 0;
+
+ for (let i = 0; i < 8; i++) {
+ num += parseInt(hash.substring(i * 4, i * 4 + 4), 16);
+ }
+
+ const index = num % pallette.length;
+
+ return (
+
+ );
+}
+
+export default Avatar;
diff --git a/src/components/input/TeamsButton.module.css b/src/components/input/TeamsButton.module.css
new file mode 100644
index 00000000..5edee957
--- /dev/null
+++ b/src/components/input/TeamsButton.module.css
@@ -0,0 +1,17 @@
+.button {
+ margin-right: 10px;
+}
+
+.menu {
+ background: var(--base50);
+ right: -10px;
+}
+
+.heading {
+ color: var(--base600);
+ font-size: 10px;
+ font-weight: 700;
+ padding: 8px 16px;
+ text-transform: uppercase;
+ border-bottom: 1px solid var(--base300);
+}
diff --git a/src/components/input/TeamsButton.tsx b/src/components/input/TeamsButton.tsx
new file mode 100644
index 00000000..4cd5c50c
--- /dev/null
+++ b/src/components/input/TeamsButton.tsx
@@ -0,0 +1,51 @@
+import { Key } from 'react';
+import { Text, Icon, Button, Popup, Menu, Item, PopupTrigger, Flexbox } from 'react-basics';
+import Icons from 'components/icons';
+import { useLogin, useMessages, useNavigation } from 'components/hooks';
+import Avatar from 'components/common/Avatar';
+import styles from './TeamsButton.module.css';
+
+export function TeamsButton({ teamId }: { teamId: string }) {
+ const { user } = useLogin();
+ const { formatMessage, labels } = useMessages();
+ const { router } = useNavigation();
+
+ const handleSelect = (close: () => void, id: Key) => {
+ if (id !== user.id) {
+ router.push(`/teams/${id}`);
+ } else {
+ router.push('/');
+ }
+ close();
+ };
+
+ return (
+
+
+
+ {close => (
+
+ )}
+
+
+ );
+}
+
+export default TeamsButton;
diff --git a/src/components/messages.ts b/src/components/messages.ts
index 15d2c442..ffa5f0d9 100644
--- a/src/components/messages.ts
+++ b/src/components/messages.ts
@@ -201,6 +201,7 @@ export const labels = defineMessages({
defaultMessage: '{x} {x, plural, one {record} other {records}}',
},
select: { id: 'label.select', defaultMessage: 'Select' },
+ myAccount: { id: 'label.my-account', defaultMessage: 'My account' },
});
export const messages = defineMessages({
diff --git a/src/declaration.d.ts b/src/declaration.d.ts
index 2be86264..2f494dc2 100644
--- a/src/declaration.d.ts
+++ b/src/declaration.d.ts
@@ -1,3 +1,4 @@
declare module 'cors';
declare module 'debug';
declare module 'chartjs-adapter-date-fns';
+declare module 'md5';
diff --git a/yarn.lock b/yarn.lock
index a49c8003..cfe307cd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3226,6 +3226,11 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
+charenc@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
+ integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
+
chart.js@^4.2.1:
version "4.4.1"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.4.1.tgz#ac5dc0e69a7758909158a96fe80ce43b3bb96a9f"
@@ -3488,6 +3493,11 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
+crypt@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
+ integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==
+
css-blank-pseudo@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz#36523b01c12a25d812df343a32c322d2a2324561"
@@ -5330,6 +5340,11 @@ is-boolean-object@^1.1.0:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
+is-buffer@~1.1.6:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+ integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
is-builtin-module@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169"
@@ -6107,6 +6122,15 @@ maxmind@^4.3.6:
mmdb-lib "2.1.0"
tiny-lru "11.2.5"
+md5@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
+ integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==
+ dependencies:
+ charenc "0.0.2"
+ crypt "0.0.2"
+ is-buffer "~1.1.6"
+
mdn-data@2.0.14:
version "2.0.14"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"