Add members while Transferring Roadmap

feat/members
Arik Chakma 1 year ago
parent 29cff6a6f8
commit 3a01e5c349
  1. 65
      src/components/ShareOptions/ShareOptionsModal.tsx
  2. 30
      src/components/ShareOptions/ShareTeamMemberList.tsx
  3. 22
      src/components/ShareOptions/TransferToTeamList.tsx

@ -1,4 +1,4 @@
import { type ReactNode, useCallback, useState } from 'react';
import { type ReactNode, useCallback, useState, useMemo } from 'react';
import { Globe2, Loader2, Lock } from 'lucide-react';
import { type ListFriendsResponse, ShareFriendList } from './ShareFriendList';
import { TransferToTeamList } from './TransferToTeamList';
@ -49,7 +49,10 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
const [isSettingsUpdated, setIsSettingsUpdated] = useState(false);
const [friends, setFriends] = useState<ListFriendsResponse>([]);
const [teams, setTeams] = useState<UserTeamItem[]>([]);
const [members, setMembers] = useState<TeamMemberList[]>([]);
// Using global team members loading state to avoid glitchy UI when switching between teams
const [isTeamMembersLoading, setIsTeamMembersLoading] = useState(false);
const membersCache = useMemo(() => new Map<string, TeamMemberList[]>(), []);
const [visibility, setVisibility] = useState(defaultVisibility);
const [sharedTeamMemberIds, setSharedTeamMemberIds] = useState<string[]>(
@ -118,7 +121,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
};
const handleTransferToTeam = useCallback(
async (teamId: string) => {
async (teamId: string, sharedTeamMemberIds: string[]) => {
if (!roadmapId) {
return;
}
@ -128,6 +131,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
`${import.meta.env.PUBLIC_API_URL}/v1-transfer-roadmap/${roadmapId}`,
{
teamId,
sharedTeamMemberIds,
}
);
@ -187,6 +191,7 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
defaultSharedFriendIds.length > 0 ? defaultSharedFriendIds : []
);
} else if (visibility === 'team' && teamId) {
setIsTeamMembersLoading(true);
setSharedTeamMemberIds(
defaultSharedMemberIds?.length > 0 ? defaultSharedMemberIds : []
);
@ -225,24 +230,50 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
setSharedFriendIds={setSharedFriendIds}
/>
)}
{/* For Team Roadmap */}
{visibility === 'team' && teamId && (
<ShareTeamMemberList
teamId={teamId}
sharedTeamMemberIds={sharedTeamMemberIds}
setSharedTeamMemberIds={setSharedTeamMemberIds}
membersCache={membersCache}
isTeamMembersLoading={isTeamMembersLoading}
setIsTeamMembersLoading={setIsTeamMembersLoading}
/>
)}
{canTransferRoadmap && (
<>
<TransferToTeamList
teams={teams}
setTeams={setTeams}
selectedTeamId={selectedTeamId}
setSelectedTeamId={setSelectedTeamId}
isTeamMembersLoading={isTeamMembersLoading}
setIsTeamMembersLoading={setIsTeamMembersLoading}
onTeamChange={() => {
setIsTeamMembersLoading(true);
setSharedTeamMemberIds([]);
}}
/>
)}
{/* For Team Roadmap */}
{visibility === 'team' && teamId && (
{selectedTeamId && (
<>
<hr className="-mx-4 my-4" />
<div className="mb-4">
<ShareTeamMemberList
teamId={teamId}
title="Select who can access this roadmap. You can change this later."
teamId={selectedTeamId!}
sharedTeamMemberIds={sharedTeamMemberIds}
setSharedTeamMemberIds={setSharedTeamMemberIds}
members={members}
setMembers={setMembers}
membersCache={membersCache}
isTeamMembersLoading={isTeamMembersLoading}
setIsTeamMembersLoading={setIsTeamMembersLoading}
/>
</div>
</>
)}
</>
)}
</div>
@ -255,17 +286,23 @@ export function ShareOptionsModal(props: ShareOptionsModalProps) {
Close
</button>
{canTransferRoadmap ? (
{canTransferRoadmap && (
<UpdateAction
disabled={isUpdateDisabled || isLoading}
disabled={
isUpdateDisabled || isLoading || sharedTeamMemberIds.length === 0
}
onClick={() => {
handleTransferToTeam(selectedTeamId!).then(() => null);
handleTransferToTeam(selectedTeamId!, sharedTeamMemberIds).then(
() => null
);
}}
>
{isLoading && <Loader2 className="h-4 w-4 animate-spin" />}
Transfer
</UpdateAction>
) : (
)}
{!canTransferRoadmap && (
<UpdateAction
disabled={isUpdateDisabled || isLoading}
onClick={() => {

@ -33,27 +33,31 @@ export interface TeamMemberList extends TeamMemberDocument {
type ShareTeamMemberListProps = {
teamId: string;
setMembers: (members: TeamMemberList[]) => void;
members: TeamMemberList[];
title?: string;
sharedTeamMemberIds: string[];
setSharedTeamMemberIds: (sharedTeamMemberIds: string[]) => void;
membersCache: Map<string, TeamMemberList[]>;
isTeamMembersLoading: boolean;
setIsTeamMembersLoading: (isLoading: boolean) => void;
};
export function ShareTeamMemberList(props: ShareTeamMemberListProps) {
const {
setMembers,
members,
teamId,
title = 'Select Members',
sharedTeamMemberIds,
setSharedTeamMemberIds,
teamId,
membersCache,
isTeamMembersLoading: isLoading,
setIsTeamMembersLoading: setIsLoading,
} = props;
const toast = useToast();
const [isLoading, setIsLoading] = useState(true);
async function loadTeamMembers() {
if (members?.length > 0) {
if (membersCache.has(teamId)) {
return;
}
@ -67,21 +71,21 @@ export function ShareTeamMemberList(props: ShareTeamMemberListProps) {
return;
}
setMembers(response);
membersCache.set(teamId, response);
}
useEffect(() => {
loadTeamMembers().finally(() => {
setIsLoading(false);
});
}, []);
}, [teamId]);
const loadingMembers = isLoading && (
<ul className="mt-2 grid grid-cols-3 gap-2.5">
{[...Array(3)].map((_, idx) => (
<li
key={idx}
className="flex min-h-[62px] animate-pulse items-center gap-2 rounded-md border p-2"
className="flex min-h-[66px] animate-pulse items-center gap-2 rounded-md border p-2"
>
<div className="h-8 w-8 shrink-0 rounded-full bg-gray-200" />
<div className="inline-grid w-full">
@ -93,11 +97,13 @@ export function ShareTeamMemberList(props: ShareTeamMemberListProps) {
</ul>
);
const members = membersCache.get(teamId) || [];
return (
<>
{(members.length > 0 || isLoading) && (
<div className="flex items-center justify-between gap-2">
<p className="text-sm">Select Members</p>
<p className="text-sm">{title}</p>
<label className="flex items-center gap-2">
<input

@ -11,10 +11,22 @@ type TransferToTeamListProps = {
selectedTeamId: string | null;
setSelectedTeamId: (teamId: string | null) => void;
isTeamMembersLoading: boolean;
setIsTeamMembersLoading: (isLoading: boolean) => void;
onTeamChange: (teamId: string | null) => void;
};
export function TransferToTeamList(props: TransferToTeamListProps) {
const { teams, setTeams, selectedTeamId, setSelectedTeamId } = props;
const {
teams,
setTeams,
selectedTeamId,
setSelectedTeamId,
isTeamMembersLoading,
setIsTeamMembersLoading,
onTeamChange,
} = props;
const toast = useToast();
@ -73,11 +85,17 @@ export function TransferToTeamList(props: TransferToTeamListProps) {
<li key={team._id}>
<button
className={cn(
'relative flex w-full items-center gap-2.5 rounded-lg border p-2.5',
'relative flex w-full items-center gap-2.5 rounded-lg border p-2.5 disabled:cursor-not-allowed disabled:opacity-70',
isSelected && 'border-gray-500 bg-gray-100 text-black'
)}
disabled={isTeamMembersLoading}
onClick={() => {
if (isSelected) {
setSelectedTeamId(null);
} else {
setSelectedTeamId(team._id);
}
onTeamChange(team._id);
}}
>
<img

Loading…
Cancel
Save