feat: Send progress reminder

chore/team-member
Arik Chakma 1 year ago
parent 8a16de9a70
commit 78941763e6
  1. 2
      src/components/TeamMembers/RoleBadge.tsx
  2. 78
      src/components/TeamMembers/TeamMemberItem.tsx
  3. 58
      src/components/TeamMembers/TeamMembersPage.tsx

@ -3,7 +3,7 @@ import type { AllowedRoles } from '../CreateTeam/RoleDropdown';
export function MemberRoleBadge({ role }: { role: AllowedRoles }) { export function MemberRoleBadge({ role }: { role: AllowedRoles }) {
return ( return (
<span <span
className={`rounded-full px-2 py-0.5 text-xs flex items-center capitalize ${['admin'].includes(role) className={`rounded-full px-2 py-0.5 text-xs sm:flex items-center capitalize ${['admin'].includes(role)
? 'bg-blue-100 text-blue-700 ' ? 'bg-blue-100 text-blue-700 '
: 'bg-gray-100 text-gray-700 ' : 'bg-gray-100 text-gray-700 '
} ${['manager'].includes(role) ? 'bg-green-100 text-green-700' : ''}`} } ${['manager'].includes(role) ? 'bg-green-100 text-green-700' : ''}`}

@ -1,7 +1,7 @@
import { MailIcon } from "../ReactIcons/MailIcon"; import { MailIcon } from '../ReactIcons/MailIcon';
import { MemberActionDropdown } from "./MemberActionDropdown"; import { MemberActionDropdown } from './MemberActionDropdown';
import { MemberRoleBadge } from "./RoleBadge"; import { MemberRoleBadge } from './RoleBadge';
import type { TeamMemberItem } from "./TeamMembersPage"; import type { TeamMemberItem } from './TeamMembersPage';
type TeamMemberProps = { type TeamMemberProps = {
member: TeamMemberItem; member: TeamMemberItem;
@ -11,42 +11,59 @@ type TeamMemberProps = {
canManageCurrentTeam: boolean; canManageCurrentTeam: boolean;
handleDeleteMember: () => void; handleDeleteMember: () => void;
onUpdateMember: () => void; onUpdateMember: () => void;
handleSendReminder: () => void;
}; };
export function TeamMemberItem(props: TeamMemberProps) { export function TeamMemberItem(props: TeamMemberProps) {
const { member, index, teamId, onUpdateMember, canManageCurrentTeam, userId, handleDeleteMember } = props; const {
const showNoProgress = member.progress.length === 0 && member.status === 'joined'; member,
index,
teamId,
onUpdateMember,
canManageCurrentTeam,
userId,
handleDeleteMember,
handleSendReminder,
} = props;
const showNoProgress =
member.progress.length === 0 && member.status === 'joined';
const showReminder =
member.progress.length === 0 &&
member.status === 'joined' &&
!(member.userId === userId);
return ( return (
<div <div
className={`flex items-center justify-between gap-2 p-3 ${index === 0 ? '' : 'border-t' className={`flex items-center justify-between gap-2 p-3 ${
index === 0 ? '' : 'border-t'
}`} }`}
> >
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<img <img
src={ src={
member.avatar member.avatar
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${member.avatar ? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${member.avatar}`
}`
: '/images/default-avatar.png' : '/images/default-avatar.png'
} }
alt={member.name || ''} alt={member.name || ''}
className="hidden h-10 w-10 rounded-full sm:block" className="hidden h-10 w-10 rounded-full sm:block"
/> />
<div> <div>
<span class={'mb-1 block sm:hidden'}> <div className="mb-1 flex items-center gap-2 sm:hidden">
<MemberRoleBadge role={member.role} /> <MemberRoleBadge role={member.role} />
</span> {showReminder && (
<SendProgressReminder handleSendReminder={handleSendReminder} />
)}
</div>
<div className="flex items-center"> <div className="flex items-center">
<h3 className="inline-grid grid-cols-[auto_auto_auto] items-center font-medium"> <h3 className="inline-grid grid-cols-[auto_auto_auto] items-center font-medium">
<span className="truncate">{member.name}</span> <span className="truncate">{member.name}</span>
{ {showNoProgress && (
showNoProgress && ( <span className="ml-2 rounded-full bg-gray-600 px-2 py-0.5 text-xs font-normal text-white sm:inline">
<span className="ml-2 text-xs font-normal bg-gray-600 text-white rounded-full sm:inline px-2 py-0.5">
No Progress No Progress
</span> </span>
) )}
}
{member.userId === userId && ( {member.userId === userId && (
<span className="ml-2 hidden text-xs font-normal text-blue-500 sm:inline"> <span className="ml-2 hidden text-xs font-normal text-blue-500 sm:inline">
You You
@ -66,14 +83,18 @@ export function TeamMemberItem(props: TeamMemberProps) {
)} )}
</div> </div>
</div> </div>
<p className="text-sm text-gray-500"> <p className="truncate text-sm text-gray-500">
{member.invitedEmail} {member.invitedEmail}
</p> </p>
</div> </div>
</div> </div>
<div className="flex items-center text-sm"> <div className="flex shrink-0 items-center text-sm">
{/* <SendProgressReminder /> */} {showReminder && (
<span className="hidden sm:block">
<SendProgressReminder handleSendReminder={handleSendReminder} />
</span>
)}
<span class={'hidden sm:block'}> <span class={'hidden sm:block'}>
<MemberRoleBadge role={member.role} /> <MemberRoleBadge role={member.role} />
</span> </span>
@ -87,14 +108,23 @@ export function TeamMemberItem(props: TeamMemberProps) {
)} )}
</div> </div>
</div> </div>
) );
} }
function SendProgressReminder() { type SendProgressReminderProps = {
handleSendReminder: () => void;
};
function SendProgressReminder(props: SendProgressReminderProps) {
const { handleSendReminder } = props;
return ( return (
<button className="rounded-full whitespace-nowrap px-2 py-0.5 text-xs bg-orange-100 text-orange-700 flex items-center gap-1.5 mr-2"> <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"
>
<MailIcon className="h-3 w-3" /> <MailIcon className="h-3 w-3" />
Send Reminder <span>Reminder</span>
</button> </button>
) );
} }

@ -1,5 +1,5 @@
import { useEffect, useState } from 'preact/hooks'; import { useEffect, useState } from 'preact/hooks';
import { httpDelete, httpGet } from '../../lib/http'; import { httpDelete, httpGet, httpPatch } from '../../lib/http';
import { MemberActionDropdown } from './MemberActionDropdown'; import { MemberActionDropdown } from './MemberActionDropdown';
import { useAuth } from '../../hooks/use-auth'; import { useAuth } from '../../hooks/use-auth';
import { pageProgressMessage } from '../../stores/page'; import { pageProgressMessage } from '../../stores/page';
@ -94,11 +94,11 @@ export function TeamMembersPage() {
pageProgressMessage.set(''); pageProgressMessage.set('');
}); });
}, [teamId]); }, [teamId]);
async function deleteMember(teamId: string, memberId: string) { async function deleteMember(teamId: string, memberId: string) {
pageProgressMessage.set('Deleting member'); pageProgressMessage.set('Deleting member');
const { response, error } = await httpDelete( const { response, error } = await httpDelete(
`${import.meta.env.PUBLIC_API_URL `${
import.meta.env.PUBLIC_API_URL
}/v1-delete-member/${teamId}/${memberId}`, }/v1-delete-member/${teamId}/${memberId}`,
{} {}
); );
@ -112,6 +112,23 @@ export function TeamMembersPage() {
await getTeamMemberList(); await getTeamMemberList();
} }
async function handleSendReminder(teamId: string, memberId: string) {
pageProgressMessage.set('Sending Reminder');
const { response, error } = await httpPatch(
`${
import.meta.env.PUBLIC_API_URL
}/v1-send-progress-reminder/${teamId}/${memberId}`,
{}
);
if (error || !response) {
toast.error(error?.message || 'Something went wrong');
return;
}
toast.success('Reminder has been sent');
}
const joinedMembers = teamMembers.filter( const joinedMembers = teamMembers.filter(
(member) => member.status === 'joined' (member) => member.status === 'joined'
); );
@ -180,14 +197,20 @@ export function TeamMembersPage() {
onUpdateMember={() => { onUpdateMember={() => {
setMemberToUpdate(member); setMemberToUpdate(member);
}} }}
handleSendReminder={() => {
handleSendReminder(teamId, member._id!).finally(() => {
pageProgressMessage.set('');
});
}}
/> />
); );
})} })}
</div> </div>
{invitedMembers.length > 0 && (<div className="mt-6"> {invitedMembers.length > 0 && (
<h3 className="font-medium text-xl">Invited Members</h3> <div className="mt-6">
<div className="rounded-b-sm rounded-t-md border mt-2"> <h3 className="text-xl font-medium">Invited Members</h3>
<div className="mt-2 rounded-b-sm rounded-t-md border">
{invitedMembers.map((member, index) => { {invitedMembers.map((member, index) => {
return ( return (
<TeamMemberItem <TeamMemberItem
@ -205,17 +228,22 @@ export function TeamMembersPage() {
onUpdateMember={() => { onUpdateMember={() => {
setMemberToUpdate(member); setMemberToUpdate(member);
}} }}
handleSendReminder={() => {
handleSendReminder(teamId, member._id!).finally(() => {
pageProgressMessage.set('');
});
}}
/> />
); );
})} })}
</div> </div>
</div>)} </div>
)}
{ {rejectedMembers.length > 0 && (
rejectedMembers.length > 0 && (
<div className="mt-6"> <div className="mt-6">
<h3 className="font-medium text-xl">Rejected Members</h3> <h3 className="text-xl font-medium">Rejected Members</h3>
<div className="rounded-b-sm rounded-t-md border mt-2"> <div className="mt-2 rounded-b-sm rounded-t-md border">
{rejectedMembers.map((member, index) => { {rejectedMembers.map((member, index) => {
return ( return (
<TeamMemberItem <TeamMemberItem
@ -233,13 +261,17 @@ export function TeamMembersPage() {
onUpdateMember={() => { onUpdateMember={() => {
setMemberToUpdate(member); setMemberToUpdate(member);
}} }}
handleSendReminder={() => {
handleSendReminder(teamId, member._id!).finally(() => {
pageProgressMessage.set('');
});
}}
/> />
); );
})} })}
</div> </div>
</div> </div>
) )}
}
</div> </div>
{canManageCurrentTeam && ( {canManageCurrentTeam && (

Loading…
Cancel
Save