Updates to team functionality

pull/4269/head
Kamran Ahmed 1 year ago
parent fc8ce296be
commit 5b541dfb3d
  1. 5
      src/components/CreateTeam/RoleDropdown.tsx
  2. 40
      src/components/TeamMembers/MemberActionDropdown.tsx
  3. 42
      src/components/TeamMembers/TeamMemberItem.tsx
  4. 24
      src/components/TeamMembers/TeamMembersPage.tsx
  5. 8
      src/components/TeamSettings/UpdateTeamForm.tsx
  6. 2
      src/components/Toast.tsx

@ -86,10 +86,7 @@ export function RoleDropdown(props: RoleDropdownProps) {
}`}
>
<span
className={`capitalize ${
selectedRole === 'admin' ? 'text-blue-600' : ''
} ${selectedRole === 'manager' ? 'text-cyan-600' : ''}`}
>
className={`capitalize`}>
{selectedRole || 'Select Role'}
</span>
<ChevronDownIcon

@ -1,9 +1,9 @@
import { useRef, useState } from 'preact/hooks';
import type { TeamMemberDocument } from './TeamMembersPage';
import { httpDelete, httpPatch } from '../../lib/http';
import MoreIcon from '../../icons/more-vertical.svg';
import { useOutsideClick } from '../../hooks/use-outside-click';
import { useToast } from '../../hooks/use-toast';
import { MailIcon } from '../ReactIcons/MailIcon';
export function MemberActionDropdown({
member,
@ -11,14 +11,19 @@ export function MemberActionDropdown({
onDeleteMember,
onResendInvite,
isDisabled = false,
onSendProgressReminder,
allowProgressReminder = false,
allowUpdateRole = true,
}: {
onDeleteMember: () => void;
onUpdateMember: () => void;
onResendInvite: () => void;
onSendProgressReminder: () => void;
isDisabled: boolean;
allowProgressReminder: boolean;
allowUpdateRole: boolean;
member: TeamMemberDocument;
}) {
const toast = useToast();
const menuRef = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
@ -35,13 +40,28 @@ export function MemberActionDropdown({
setIsOpen(false);
},
},
{
name: 'Update Role',
handleClick: () => {
onUpdateMember();
setIsOpen(false);
},
},
...(allowUpdateRole
? [
{
name: 'Update Role',
handleClick: () => {
onUpdateMember();
setIsOpen(false);
},
},
]
: []),
...(allowProgressReminder
? [
{
name: 'Send Progress Reminder',
handleClick: () => {
onSendProgressReminder();
setIsOpen(false);
},
},
]
: []),
...(['invited'].includes(member.status)
? [
{
@ -67,7 +87,7 @@ export function MemberActionDropdown({
{isOpen && (
<div
ref={menuRef}
className="align-right absolute right-0 top-full z-50 mt-1 w-32 rounded-md bg-slate-800 px-2 py-2 text-white shadow-md"
className="align-right absolute right-0 top-full z-50 mt-1 w-[200px] rounded-md bg-slate-800 px-2 py-2 text-white shadow-md"
>
<ul>
{actions.map((action, index) => {

@ -2,6 +2,8 @@ import { MailIcon } from '../ReactIcons/MailIcon';
import { MemberActionDropdown } from './MemberActionDropdown';
import { MemberRoleBadge } from './RoleBadge';
import type { TeamMemberItem } from './TeamMembersPage';
import { $canManageCurrentTeam } from '../../stores/team';
import { useStore } from '@nanostores/preact';
type TeamMemberProps = {
member: TeamMemberItem;
@ -9,9 +11,9 @@ type TeamMemberProps = {
index: number;
teamId: string;
canManageCurrentTeam: boolean;
handleDeleteMember: () => void;
onDeleteMember: () => void;
onUpdateMember: () => void;
handleSendReminder: () => void;
onSendProgressReminder: () => void;
onResendInvite: () => void;
};
@ -23,16 +25,17 @@ export function TeamMemberItem(props: TeamMemberProps) {
onUpdateMember,
canManageCurrentTeam,
userId,
handleDeleteMember,
handleSendReminder,
onDeleteMember,
onSendProgressReminder,
} = props;
const showNoProgress =
member.progress.length === 0 && member.status === 'joined';
const showReminder =
member.progress.length === 0 &&
const canManageTeam = useStore($canManageCurrentTeam);
const showNoProgressBadge = !member.hasProgress && member.status === 'joined';
const allowProgressReminder =
canManageTeam &&
!member.hasProgress &&
member.status === 'joined' &&
!(member.userId === userId);
member.userId !== userId;
return (
<div
@ -53,15 +56,12 @@ export function TeamMemberItem(props: TeamMemberProps) {
<div>
<div className="mb-1 flex items-center gap-2 sm:hidden">
<MemberRoleBadge role={member.role} />
{showReminder && (
<SendProgressReminder handleSendReminder={handleSendReminder} />
)}
</div>
<div className="flex items-center">
<h3 className="inline-grid grid-cols-[auto_auto_auto] items-center font-medium">
<span className="truncate">{member.name}</span>
{showNoProgress && (
<span className="ml-2 rounded-full bg-gray-600 px-2 py-0.5 text-xs font-normal text-white sm:inline">
{showNoProgressBadge && (
<span className="ml-2 rounded-full bg-red-400 px-2 py-0.5 text-xs font-normal text-white">
No Progress
</span>
)}
@ -91,18 +91,16 @@ export function TeamMemberItem(props: TeamMemberProps) {
</div>
<div className="flex shrink-0 items-center text-sm">
{showReminder && (
<span className="hidden sm:block">
<SendProgressReminder handleSendReminder={handleSendReminder} />
</span>
)}
<span class={'hidden sm:block'}>
<MemberRoleBadge role={member.role} />
</span>
{canManageCurrentTeam && (
<MemberActionDropdown
allowUpdateRole={member.status !== 'rejected'}
allowProgressReminder={allowProgressReminder}
onResendInvite={onResendInvite}
onDeleteMember={handleDeleteMember}
onSendProgressReminder={onSendProgressReminder}
onDeleteMember={onDeleteMember}
isDisabled={member.userId === userId}
onUpdateMember={onUpdateMember}
member={member}
@ -123,10 +121,10 @@ function SendProgressReminder(props: SendProgressReminderProps) {
return (
<button
onClick={handleSendReminder}
className="mr-2 flex items-center gap-1.5 whitespace-nowrap rounded-full bg-orange-100 px-2 py-0.5 text-xs text-orange-700"
className="ml-2 flex items-center gap-1.5 whitespace-nowrap rounded-full bg-orange-100 px-2 py-0.5 text-xs text-orange-700"
>
<MailIcon className="h-3 w-3" />
<span>Reminder</span>
<span>Remind</span>
</button>
);
}

@ -1,6 +1,5 @@
import { useEffect, useState } from 'preact/hooks';
import { httpDelete, httpGet, httpPatch } from '../../lib/http';
import { MemberActionDropdown } from './MemberActionDropdown';
import { useAuth } from '../../hooks/use-auth';
import { pageProgressMessage } from '../../stores/page';
import type { TeamDocument } from '../CreateTeam/CreateTeamForm';
@ -13,7 +12,6 @@ import { UpdateMemberPopup } from './UpdateMemberPopup';
import { useStore } from '@nanostores/preact';
import { $canManageCurrentTeam } from '../../stores/team';
import { useToast } from '../../hooks/use-toast';
import { MemberRoleBadge } from './RoleBadge';
import { TeamMemberItem } from './TeamMemberItem';
export interface TeamMemberDocument {
@ -43,7 +41,7 @@ export interface UserResourceProgressDocument {
export interface TeamMemberItem extends TeamMemberDocument {
name: string;
avatar: string;
progress: UserResourceProgressDocument[];
hasProgress: boolean;
}
export function TeamMembersPage() {
@ -187,7 +185,7 @@ export function TeamMembersPage() {
/>
)}
<div>
<div className="rounded-b-sm rounded-t-md border">
<div className="rounded-md border">
<div className="flex items-center justify-between gap-2 border-b p-3">
<p className="hidden text-sm sm:block">
{teamMembers.length} people in the team.
@ -211,7 +209,7 @@ export function TeamMembersPage() {
});
}}
canManageCurrentTeam={canManageCurrentTeam}
handleDeleteMember={() => {
onDeleteMember={() => {
deleteMember(teamId, member._id!).finally(() => {
pageProgressMessage.set('');
});
@ -219,7 +217,7 @@ export function TeamMembersPage() {
onUpdateMember={() => {
setMemberToUpdate(member);
}}
handleSendReminder={() => {
onSendProgressReminder={() => {
handleSendReminder(teamId, member._id!).finally(() => {
pageProgressMessage.set('');
});
@ -231,8 +229,8 @@ export function TeamMembersPage() {
{invitedMembers.length > 0 && (
<div className="mt-6">
<h3 className="text-xl font-medium">Invited Members</h3>
<div className="mt-2 rounded-b-sm rounded-t-md border">
<h3 className="text-xs uppercase text-gray-400">Invited Members</h3>
<div className="mt-2 rounded-md border">
{invitedMembers.map((member, index) => {
return (
<TeamMemberItem
@ -247,7 +245,7 @@ export function TeamMembersPage() {
});
}}
canManageCurrentTeam={canManageCurrentTeam}
handleDeleteMember={() => {
onDeleteMember={() => {
deleteMember(teamId, member._id!).finally(() => {
pageProgressMessage.set('');
});
@ -255,7 +253,7 @@ export function TeamMembersPage() {
onUpdateMember={() => {
setMemberToUpdate(member);
}}
handleSendReminder={() => {
onSendProgressReminder={() => {
handleSendReminder(teamId, member._id!).finally(() => {
pageProgressMessage.set('');
});
@ -269,7 +267,7 @@ export function TeamMembersPage() {
{rejectedMembers.length > 0 && (
<div className="mt-6">
<h3 className="text-xl font-medium">Rejected Members</h3>
<h3 className="text-xs uppercase text-gray-400">Rejected Invites</h3>
<div className="mt-2 rounded-b-sm rounded-t-md border">
{rejectedMembers.map((member, index) => {
return (
@ -285,7 +283,7 @@ export function TeamMembersPage() {
});
}}
canManageCurrentTeam={canManageCurrentTeam}
handleDeleteMember={() => {
onDeleteMember={() => {
deleteMember(teamId, member._id!).finally(() => {
pageProgressMessage.set('');
});
@ -293,7 +291,7 @@ export function TeamMembersPage() {
onUpdateMember={() => {
setMemberToUpdate(member);
}}
handleSendReminder={() => {
onSendProgressReminder={() => {
handleSendReminder(teamId, member._id!).finally(() => {
pageProgressMessage.set('');
});

@ -141,10 +141,16 @@ export function UpdateTeamForm() {
/>
</div>
<div className="mt-4 flex w-full flex-col">
<label for="website" className="text-sm leading-none text-slate-500">
<label
for="website"
className={`text-sm leading-none text-slate-500 ${
teamType === 'company' ? 'after:content-["*"]' : ''
}`}
>
Website
</label>
<input
required={teamType === 'company'}
type="text"
name="website"
id="website"

@ -51,7 +51,7 @@ export function Toaster(props: Props) {
onClick={() => {
$toastMessage.set(undefined);
}}
className={`fixed bottom-5 left-1/2 z-50 min-w-[300px] max-w-[300px] animate-fade-slide-up sm:min-w-[auto]`}
className={`fixed bottom-5 left-1/2 z-50 min-w-[375px] max-w-[375px] animate-fade-slide-up sm:min-w-[auto]`}
>
<div
className={`flex -translate-x-1/2 transform cursor-pointer items-center gap-2 rounded-md border border-gray-200 bg-white py-3 pl-4 pr-5 text-black shadow-md hover:bg-gray-50`}

Loading…
Cancel
Save