From 992342aa7275111082da1e054fe4c043059c90c5 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 31 Jan 2024 02:30:49 -0800 Subject: [PATCH] Added teams button and Avatar component. --- package.json | 1 + src/app/(main)/NavBar.tsx | 11 +- src/components/common/Avatar.tsx | 158 ++++++++++++++++++++ src/components/input/TeamsButton.module.css | 17 +++ src/components/input/TeamsButton.tsx | 51 +++++++ src/components/messages.ts | 1 + src/declaration.d.ts | 1 + yarn.lock | 24 +++ 8 files changed, 260 insertions(+), 4 deletions(-) create mode 100644 src/components/common/Avatar.tsx create mode 100644 src/components/input/TeamsButton.module.css create mode 100644 src/components/input/TeamsButton.tsx 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 => ( + +
{formatMessage(labels.myAccount)}
+ {user.username} +
{formatMessage(labels.team)}
+ {user.teams.map(({ id, name }) => ( + + + + + + {name} + + + ))} +
+ )} +
+
+ ); +} + +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"