Updating personal progress from popup

pull/4259/head
Kamran Ahmed 1 year ago
parent b85639d876
commit f48a351c99
  1. 38
      src/components/TeamProgress/GroupRoadmapItem.tsx
  2. 22
      src/components/TeamProgress/MemberProgressItem.tsx
  3. 62
      src/components/TeamProgress/MemberProgressModal.tsx
  4. 3
      src/components/TeamProgress/ProgressHint.tsx
  5. 63
      src/components/TeamProgress/TeamProgressPage.tsx

@ -1,37 +1,25 @@
import { useState } from 'preact/hooks';
import type { GroupByRoadmap, TeamMember } from './TeamProgressPage';
import { MemberProgressModal } from './MemberProgressModal';
import { getUrlParams } from '../../lib/browser';
import ExternalLinkIcon from '../../icons/external-link.svg';
import { useAuth } from '../../hooks/use-auth';
type GroupRoadmapItemProps = {
roadmap: GroupByRoadmap;
onShowResourceProgress: (member: TeamMember, resourceId: string) => void;
};
export function GroupRoadmapItem(props: GroupRoadmapItemProps) {
const { onShowResourceProgress } = props;
const { members, resourceTitle, resourceId } = props.roadmap;
const { t: teamId } = getUrlParams();
const user = useAuth();
const [showAll, setShowAll] = useState(false);
const [detailResourceId, setDetailResourceId] = useState<string | null>(null);
const [selectedMember, setSelectedMember] = useState<TeamMember | null>(null);
return (
<>
{detailResourceId && (
<MemberProgressModal
member={selectedMember!}
teamId={teamId}
resourceId={detailResourceId}
resourceType={'roadmap'}
onClose={() => {
setDetailResourceId(null);
setSelectedMember(null);
}}
/>
)}
<div className="flex h-full min-h-[270px] flex-col rounded-md border">
<div className="flex items-center gap-3 border-b p-3">
<div className="flex min-w-0 flex-grow items-center justify-between">
@ -59,11 +47,15 @@ export function GroupRoadmapItem(props: GroupRoadmapItemProps) {
return (
<button
className={`group relative w-full overflow-hidden rounded-md border p-2 hover:border-gray-300 hover:text-black focus:outline-none ${isMyProgress ? 'border-green-500 hover:border-green-600' : ''}`}
className={`group relative w-full overflow-hidden rounded-md border p-2 hover:border-gray-300 hover:text-black focus:outline-none ${
isMyProgress ? 'border-green-500 hover:border-green-600' : ''
}`}
key={member?.member._id}
onClick={() => {
setDetailResourceId(member?.progress?.resourceId!);
setSelectedMember(member.member);
onShowResourceProgress(
member.member,
member.progress?.resourceId!
);
}}
>
<span className="relative z-10 flex items-center justify-between gap-1 text-sm">
@ -80,7 +72,9 @@ export function GroupRoadmapItem(props: GroupRoadmapItemProps) {
className="h-5 w-5 shrink-0 rounded-full"
/>
<span className="inline-grid grid-cols-[auto,32px] items-center">
<span className="truncate mr-[5px]">{member?.member?.name}</span>
<span className="mr-[5px] truncate">
{member?.member?.name}
</span>
</span>
</span>
<span className="shrink-0 text-xs text-gray-400">
@ -88,7 +82,11 @@ export function GroupRoadmapItem(props: GroupRoadmapItemProps) {
</span>
</span>
<span
className={`absolute inset-0 ${isMyProgress ? 'bg-green-100 group-hover:bg-green-200' : 'bg-gray-100 group-hover:bg-gray-200'}`}
className={`absolute inset-0 ${
isMyProgress
? 'bg-green-100 group-hover:bg-green-200'
: 'bg-gray-100 group-hover:bg-gray-200'
}`}
style={{
width: `${
(member?.progress?.done / member?.progress?.total) * 100

@ -1,36 +1,22 @@
import type { TeamMember } from './TeamProgressPage';
import { useState } from 'preact/hooks';
import { MemberProgressModal } from './MemberProgressModal';
type MemberProgressItemProps = {
teamId: string;
member: TeamMember;
onShowResourceProgress: (resourceId: string) => void;
isMyProgress?: boolean;
};
export function MemberProgressItem(props: MemberProgressItemProps) {
const { member, teamId, isMyProgress = false } = props;
const { member, onShowResourceProgress, isMyProgress = false } = props;
const memberProgress = member?.progress?.sort((a, b) => {
return b.done - a.done;
});
const [detailResourceId, setDetailResourceId] = useState<string | null>(null);
const [showAll, setShowAll] = useState(false);
return (
<>
{detailResourceId && (
<MemberProgressModal
member={member}
teamId={teamId}
resourceId={detailResourceId}
resourceType={'roadmap'}
onClose={() => {
setDetailResourceId(null);
}}
/>
)}
<div
className={`flex h-full min-h-[270px] flex-col overflow-hidden rounded-md border`}
key={member._id}
@ -52,7 +38,7 @@ export function MemberProgressItem(props: MemberProgressItemProps) {
{isMyProgress && (
<div className="inline-grid grid-cols-[auto,32px] items-center gap-1.5">
<h3 className="truncate font-medium">{member.name}</h3>
<span className="rounded-md bg-red-500 py-0.5 px-1 text-xs text-white">
<span className="rounded-md bg-red-500 px-1 py-0.5 text-xs text-white">
You
</span>
</div>
@ -65,7 +51,7 @@ export function MemberProgressItem(props: MemberProgressItemProps) {
(progress) => {
return (
<button
onClick={() => setDetailResourceId(progress.resourceId)}
onClick={() => onShowResourceProgress(progress.resourceId)}
className="group relative overflow-hidden rounded-md border p-2 hover:border-gray-300 hover:text-black focus:outline-none"
key={progress.resourceId}
>

@ -7,9 +7,9 @@ import { useKeydown } from '../../hooks/use-keydown';
import type { TeamMember } from './TeamProgressPage';
import { httpGet } from '../../lib/http';
import {
renderTopicProgress,
ResourceProgressType,
ResourceType,
renderTopicProgress,
updateResourceProgress,
} from '../../lib/resource-progress';
import CloseIcon from '../../icons/close.svg';
@ -17,8 +17,6 @@ import { useToast } from '../../hooks/use-toast';
import { useAuth } from '../../hooks/use-auth';
import { pageProgressMessage } from '../../stores/page';
import { ProgressHint } from './ProgressHint';
import QuestionIcon from '../../icons/question.svg';
import { InfoIcon } from '../ReactIcons/InfoIcon';
export type ProgressMapProps = {
member: TeamMember;
@ -26,6 +24,7 @@ export type ProgressMapProps = {
resourceId: string;
resourceType: 'roadmap' | 'best-practice';
onClose: () => void;
onShowMyProgress: () => void;
};
type MemberProgressResponse = {
@ -36,7 +35,14 @@ type MemberProgressResponse = {
};
export function MemberProgressModal(props: ProgressMapProps) {
const { resourceId, member, resourceType, teamId, onClose } = props;
const {
resourceId,
member,
resourceType,
onShowMyProgress,
teamId,
onClose,
} = props;
const user = useAuth();
const isCurrentUser = user?.email === member.email;
@ -112,6 +118,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
return;
}
setIsLoading(true);
Promise.all([
renderResource(resourceJsonUrl),
getMemberProgress(teamId, member._id, resourceType, resourceId),
@ -136,7 +143,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
.finally(() => {
setIsLoading(false);
});
}, []);
}, [member]);
function updateTopicStatus(topicId: string, newStatus: ResourceProgressType) {
if (!resourceId || !resourceType || !isCurrentUser) {
@ -176,18 +183,21 @@ export function MemberProgressModal(props: ProgressMapProps) {
if (!targetGroup) {
return;
}
const groupId = targetGroup.dataset ? targetGroup.dataset.groupId : '';
if (!groupId) {
return;
}
if (targetGroup.classList.contains('removed')) {
e.preventDefault();
return;
}
e.preventDefault();
const isCurrentStatusDone = targetGroup.classList.contains('done');
const normalizedGroupId = groupId.replace(/^\d+-/, '');
updateTopicStatus(
normalizedGroupId,
!isCurrentStatusDone ? 'done' : 'pending'
@ -235,7 +245,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
}
useEffect(() => {
if (!isCurrentUser || !containerEl.current) {
if (!member || !containerEl.current) {
return;
}
@ -246,7 +256,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
containerEl.current?.removeEventListener('contextmenu', handleRightClick);
containerEl.current?.removeEventListener('click', handleClick);
};
}, []);
}, [member]);
const removedTopics = memberProgress?.removed || [];
const memberDone =
@ -282,19 +292,18 @@ export function MemberProgressModal(props: ProgressMapProps) {
{isCurrentUser ? (
<div className="mb-5 mt-0 text-left md:mt-4 md:text-center">
<h2 className={'mb-1 text-lg font-bold md:text-2xl'}>
Your Progress
Update Your Progress
</h2>
<p className={'text-gray-500'}>
You can{' '}
Follow the{' '}
<button
className="inline-flex items-center text-blue-600 underline"
onClick={() => {
setShowProgressHint(true);
}}
>
follow these instructions
instructions to update your progress
</button>{' '}
to update your progress below.
</p>
</div>
) : (
@ -308,28 +317,29 @@ export function MemberProgressModal(props: ProgressMapProps) {
}
>
You are looking at {member.name}'s progress.{' '}
<a
target={'_blank'}
href={`/${resourceId}?t=${teamId}`}
<button
className="text-blue-600 underline"
onClick={onShowMyProgress}
>
View your progress
</a>
</button>
.
</p>
<p className={'block text-gray-500 md:hidden'}>
View your progress&nbsp;
<a
target={'_blank'}
href={`/${resourceId}?t=${teamId}`}
<button
className="text-blue-600 underline"
onClick={onShowMyProgress}
>
on the roadmap page.
</a>
View your progress.
</button>
</p>
</div>
)}
<p class="-mx-4 mb-3 flex items-center justify-start border-b border-t px-4 py-2 text-sm sm:hidden">
<p
class={`-mx-4 mb-3 flex items-center justify-start border-b border-t px-4 py-2 text-sm sm:hidden ${
isLoading ? 'striped-loader' : ''
}`}
>
<span class="mr-2.5 block rounded-sm bg-yellow-200 px-1 py-0.5 text-xs font-medium uppercase text-yellow-900">
<span>{progressPercentage}</span>% Done
</span>
@ -338,7 +348,11 @@ export function MemberProgressModal(props: ProgressMapProps) {
<span>{memberDone}</span> of <span>{memberTotal}</span> done
</span>
</p>
<p class="-mx-4 mb-3 hidden items-center justify-center border-b border-t py-2 text-sm sm:flex">
<p
class={`-mx-4 mb-3 hidden items-center justify-center border-b border-t py-2 text-sm sm:flex ${
isLoading ? 'striped-loader' : ''
}`}
>
<span class="mr-2.5 block rounded-sm bg-yellow-200 px-1 py-0.5 text-xs font-medium uppercase text-yellow-900">
<span>{progressPercentage}</span>% Done
</span>
@ -385,7 +399,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
className="popup-close absolute right-2.5 top-3 ml-auto inline-flex items-center rounded-lg bg-transparent p-1.5 text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 sm:hidden"
onClick={onClose}
>
<img src={CloseIcon} className="h-4 w-4" />
<img alt={'close'} src={CloseIcon} className="h-4 w-4" />
<span class="sr-only">Close modal</span>
</button>
</div>

@ -15,11 +15,12 @@ export function ProgressHint(props: ProgressHintProps) {
useKeydown('Escape', () => {
onClose();
});
return (
<div className="fixed left-0 right-0 top-0 z-50 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50">
<div className="relative flex h-full w-full items-center justify-center">
<div
className="relative w-full max-w-lg rounded-md border border-yellow-300 bg-yellow-50 px-3 py-3 text-gray-500"
className="relative w-full max-w-lg rounded-md bg-yellow-50 px-3 py-3 text-gray-500"
ref={containerEl}
>
<span className="mb-1.5 block text-xs font-medium uppercase text-green-600">

@ -9,6 +9,7 @@ import { $currentTeam } from '../../stores/team';
import { GroupRoadmapItem } from './GroupRoadmapItem';
import { getUrlParams, setUrlParams } from '../../lib/browser';
import { useAuth } from '../../hooks/use-auth';
import { MemberProgressModal } from './MemberProgressModal';
export type UserProgress = {
resourceTitle: string;
@ -56,6 +57,11 @@ export function TeamProgressPage() {
const currentTeam = useStore($currentTeam);
const user = useAuth();
const [showMemberProgress, setShowMemberProgress] = useState<{
resourceId: string;
member: TeamMember;
}>();
const [teamMembers, setTeamMembers] = useState<TeamMember[]>([]);
const [selectedGrouping, setSelectedGrouping] = useState<
'roadmap' | 'member'
@ -88,12 +94,10 @@ export function TeamProgressPage() {
return;
}
getTeamProgress().then(
() => {
pageProgressMessage.set('');
setIsLoading(false);
}
);
getTeamProgress().then(() => {
pageProgressMessage.set('');
setIsLoading(false);
});
}, [teamId]);
if (isLoading) {
@ -143,13 +147,34 @@ export function TeamProgressPage() {
return (
<div>
{showMemberProgress && (
<MemberProgressModal
member={showMemberProgress.member}
teamId={teamId}
resourceId={showMemberProgress.resourceId}
resourceType={'roadmap'}
onClose={() => {
setShowMemberProgress(undefined);
}}
onShowMyProgress={() => {
setShowMemberProgress({
resourceId: showMemberProgress.resourceId,
member: teamMembers.find(
(member) => member.email === user?.email
)!,
});
}}
/>
)}
<div className="flex items-center gap-2">
{groupingTypes.map((grouping) => (
<button
className={`rounded-md border p-1 px-2 text-sm ${selectedGrouping === grouping.value
? ' border-gray-400 bg-gray-200 '
: ''
}`}
className={`rounded-md border p-1 px-2 text-sm ${
selectedGrouping === grouping.value
? ' border-gray-400 bg-gray-200 '
: ''
}`}
onClick={() => setSelectedGrouping(grouping.value)}
>
{grouping.label}
@ -162,7 +187,16 @@ export function TeamProgressPage() {
<div className="grid gap-4 sm:grid-cols-2">
{groupByRoadmap.map((roadmap) => {
return (
<GroupRoadmapItem key={roadmap.resourceId} roadmap={roadmap} />
<GroupRoadmapItem
key={roadmap.resourceId}
roadmap={roadmap}
onShowResourceProgress={(member, resourceId) => {
setShowMemberProgress({
resourceId,
member,
});
}}
/>
);
})}
</div>
@ -171,9 +205,14 @@ export function TeamProgressPage() {
<div className="grid gap-4 sm:grid-cols-2">
{teamMembers.map((member) => (
<MemberProgressItem
teamId={teamId}
member={member}
isMyProgress={member?.email === user?.email}
onShowResourceProgress={(resourceId) => {
setShowMemberProgress({
resourceId,
member,
});
}}
/>
))}
</div>

Loading…
Cancel
Save