mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-23 09:22:52 +02:00
Updates to team functionality
This commit is contained in:
@@ -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`}
|
||||
|
Reference in New Issue
Block a user