Fix images not working in latest astro (#4676)

* Fix respond invite

* Team roadmap icon fix

* Personal roadmap list and empty friends

* Fix invite friend

* Fix user progress modal

* Friends and notification pages

* Friends and notification pages

* Update

* Fix progress modal

---------

Co-authored-by: Arik Chakma <arikchangma@gmail.com>
pull/4680/head
Kamran Ahmed 1 year ago committed by GitHub
parent 76d1ca1333
commit 80ec1a1c4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      src/components/Activity/EmptyActivity.tsx
  2. 8
      src/components/CreateTeam/NotDropdown.tsx
  3. 9
      src/components/CustomRoadmap/PersonalRoadmapList.tsx
  4. 16
      src/components/Friends/EmptyFriends.tsx
  5. 19
      src/components/Friends/FriendsPage.tsx
  6. 8
      src/components/Friends/InviteFriendPopup.tsx
  7. 70
      src/components/Notification/NotificationPage.tsx
  8. 24
      src/components/ReactIcons/AcceptIcon.tsx
  9. 16
      src/components/RespondInviteForm.tsx
  10. 10
      src/components/TeamProgress/GroupRoadmapItem.tsx
  11. 3
      src/components/TeamProgress/MemberCustomProgressModal.tsx
  12. 19
      src/components/TeamProgress/MemberProgressModal.tsx
  13. 43
      src/components/TeamRoadmapsList/TeamRoadmaps.tsx
  14. 4
      src/components/UserProgress/UserCustomProgressModal.tsx
  15. 10
      src/components/UserProgress/UserProgressModal.tsx

@ -1,14 +1,11 @@
import RoadmapIcon from '../../icons/roadmap.svg'; import { RoadmapIcon } from "../ReactIcons/RoadmapIcon";
export function EmptyActivity() { export function EmptyActivity() {
return ( return (
<div className="rounded-md"> <div className="rounded-md">
<div className="flex flex-col items-center p-7 text-center"> <div className="flex flex-col items-center p-7 text-center">
<img <RoadmapIcon className="mb-2 w-[60px] h-[60px] sm:h-[120px] sm:w-[120px] opacity-10" />
alt="no roadmaps"
src={RoadmapIcon.src}
className="mb-2 w-[60px] h-[60px] sm:h-[120px] sm:w-[120px] opacity-10"
/>
<h2 className="text-lg sm:text-xl font-bold">No Progress</h2> <h2 className="text-lg sm:text-xl font-bold">No Progress</h2>
<p className="my-1 sm:my-2 max-w-[400px] text-gray-500 text-sm sm:text-base"> <p className="my-1 sm:my-2 max-w-[400px] text-gray-500 text-sm sm:text-base">
Progress will appear here as you start tracking your{' '} Progress will appear here as you start tracking your{' '}

@ -1,4 +1,4 @@
import ChevronDownIcon from '../../icons/chevron-down.svg'; import { ChevronDownIcon } from '../ReactIcons/ChevronDownIcon';
type NotDropdownProps = { type NotDropdownProps = {
onClick: () => void; onClick: () => void;
@ -37,11 +37,7 @@ export function NotDropdown(props: NotDropdownProps) {
</div> </div>
)} )}
<img <ChevronDownIcon className="relative top-[1px] h-[17px] w-[17px] opacity-40" />
alt={singularName}
src={ChevronDownIcon.src}
className={'relative top-[1px] h-[17px] w-[17px] opacity-40'}
/>
</div> </div>
); );
} }

@ -14,11 +14,11 @@ import {
type AllowedRoadmapVisibility, type AllowedRoadmapVisibility,
type RoadmapDocument, type RoadmapDocument,
} from './CreateRoadmap/CreateRoadmapModal'; } from './CreateRoadmap/CreateRoadmapModal';
import RoadmapIcon from '../../icons/roadmap.svg';
import { PersonalRoadmapActionDropdown } from './PersonalRoadmapActionDropdown'; import { PersonalRoadmapActionDropdown } from './PersonalRoadmapActionDropdown';
import type { GetRoadmapListResponse } from './RoadmapListPage'; import type { GetRoadmapListResponse } from './RoadmapListPage';
import { useState, type Dispatch, type SetStateAction } from 'react'; import { useState, type Dispatch, type SetStateAction } from 'react';
import { ShareOptionsModal } from '../ShareOptions/ShareOptionsModal'; import { ShareOptionsModal } from '../ShareOptions/ShareOptionsModal';
import {RoadmapIcon} from "../ReactIcons/RoadmapIcon.tsx";
type PersonalRoadmapListType = { type PersonalRoadmapListType = {
roadmaps: GetRoadmapListResponse['personalRoadmaps']; roadmaps: GetRoadmapListResponse['personalRoadmaps'];
@ -91,11 +91,8 @@ export function PersonalRoadmapList(props: PersonalRoadmapListType) {
if (roadmapList.length === 0) { if (roadmapList.length === 0) {
return ( return (
<div className="flex flex-col items-center p-4 py-20"> <div className="flex flex-col items-center p-4 py-20">
<img <RoadmapIcon className="mb-4 h-24 w-24 opacity-10" />
alt="roadmap"
src={RoadmapIcon.src}
className="mb-4 h-24 w-24 opacity-10"
/>
<h3 className="mb-1 text-2xl font-bold text-gray-900">No roadmaps</h3> <h3 className="mb-1 text-2xl font-bold text-gray-900">No roadmaps</h3>
<p className="text-base text-gray-500"> <p className="text-base text-gray-500">
Create a roadmap to get started Create a roadmap to get started

@ -1,6 +1,5 @@
import UserPlusIcon from '../../icons/user-plus.svg';
import CopyIcon from '../../icons/copy.svg';
import { useCopyText } from '../../hooks/use-copy-text'; import { useCopyText } from '../../hooks/use-copy-text';
import { CopyIcon, UserPlus2 } from 'lucide-react';
type EmptyFriendsProps = { type EmptyFriendsProps = {
befriendUrl: string; befriendUrl: string;
@ -13,14 +12,12 @@ export function EmptyFriends(props: EmptyFriendsProps) {
return ( return (
<div className="rounded-md"> <div className="rounded-md">
<div className="mx-auto flex flex-col items-center p-7 text-center"> <div className="mx-auto flex flex-col items-center p-7 text-center">
<img <UserPlus2 className="mb-2 h-[60px] w-[60px] opacity-10 sm:h-[120px] sm:w-[120px]" />
alt="no friends"
src={UserPlusIcon.src}
className="mb-2 h-[60px] w-[60px] opacity-10 sm:h-[120px] sm:w-[120px]"
/>
<h2 className="text-lg font-bold sm:text-xl">Invite your Friends</h2> <h2 className="text-lg font-bold sm:text-xl">Invite your Friends</h2>
<p className="mb-4 mt-1 max-w-[400px] text-sm leading-relaxed text-gray-500"> <p className="mb-4 mt-1 max-w-[400px] text-sm leading-relaxed text-gray-500">
Share the unique link below with your friends to track their skills and progress. Share the unique link below with your friends to track their skills
and progress.
</p> </p>
<div className="flex w-full max-w-[352px] items-center justify-center gap-2 rounded-lg border-2 p-1 text-sm"> <div className="flex w-full max-w-[352px] items-center justify-center gap-2 rounded-lg border-2 p-1 text-sm">
@ -44,7 +41,8 @@ export function EmptyFriends(props: EmptyFriendsProps) {
copyText(befriendUrl); copyText(befriendUrl);
}} }}
> >
<img src={CopyIcon.src} className="h-4 w-4" alt="Invite Friends" /> <CopyIcon className="mr-1 h-4 w-4" />
{isCopied ? 'Copied' : 'Copy'} {isCopied ? 'Copied' : 'Copy'}
</button> </button>
</div> </div>

@ -7,10 +7,10 @@ import type { FriendshipStatus } from '../Befriend';
import { useToast } from '../../hooks/use-toast'; import { useToast } from '../../hooks/use-toast';
import { EmptyFriends } from './EmptyFriends'; import { EmptyFriends } from './EmptyFriends';
import { FriendProgressItem } from './FriendProgressItem'; import { FriendProgressItem } from './FriendProgressItem';
import UserIcon from '../../icons/user.svg';
import { UserProgressModal } from '../UserProgress/UserProgressModal'; import { UserProgressModal } from '../UserProgress/UserProgressModal';
import { InviteFriendPopup } from './InviteFriendPopup'; import { InviteFriendPopup } from './InviteFriendPopup';
import { UserCustomProgressModal } from '../UserProgress/UserCustomProgressModal'; import { UserCustomProgressModal } from '../UserProgress/UserCustomProgressModal';
import { UserIcon } from '../ReactIcons/UserIcon.tsx';
type FriendResourceProgress = { type FriendResourceProgress = {
updatedAt: string; updatedAt: string;
@ -64,7 +64,7 @@ export function FriendsPage() {
async function loadFriends() { async function loadFriends() {
const { response, error } = await httpGet<ListFriendsResponse>( const { response, error } = await httpGet<ListFriendsResponse>(
`${import.meta.env.PUBLIC_API_URL}/v1-list-friends` `${import.meta.env.PUBLIC_API_URL}/v1-list-friends`,
); );
if (error || !response) { if (error || !response) {
@ -89,15 +89,15 @@ export function FriendsPage() {
const befriendUrl = `${baseUrl}/befriend?u=${user?.id}`; const befriendUrl = `${baseUrl}/befriend?u=${user?.id}`;
const selectedGroupingType = groupingTypes.find( const selectedGroupingType = groupingTypes.find(
(grouping) => grouping.value === selectedGrouping (grouping) => grouping.value === selectedGrouping,
); );
const filteredFriends = friends.filter((friend) => const filteredFriends = friends.filter(
selectedGroupingType?.statuses.includes(friend.status) (friend) => selectedGroupingType?.statuses.includes(friend.status),
); );
const receivedRequests = friends.filter( const receivedRequests = friends.filter(
(friend) => friend.status === 'received' (friend) => friend.status === 'received',
); );
if (isLoading) { if (isLoading) {
@ -203,11 +203,8 @@ export function FriendsPage() {
{filteredFriends.length === 0 && ( {filteredFriends.length === 0 && (
<div className="flex flex-col items-center justify-center py-12"> <div className="flex flex-col items-center justify-center py-12">
<img <UserIcon className="mb-3 w-12 opacity-20" />
src={UserIcon.src}
alt="Empty Friends"
className="mb-3 w-12 opacity-20"
/>
<h2 className="text-lg font-semibold"> <h2 className="text-lg font-semibold">
{selectedGrouping === 'active' && 'No friends yet'} {selectedGrouping === 'active' && 'No friends yet'}
{selectedGrouping === 'sent' && 'No requests sent'} {selectedGrouping === 'sent' && 'No requests sent'}

@ -1,8 +1,8 @@
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
import { useRef } from 'react'; import { useRef } from 'react';
import { useOutsideClick } from '../../hooks/use-outside-click'; import { useOutsideClick } from '../../hooks/use-outside-click';
import CopyIcon from '../../icons/copy.svg';
import { useCopyText } from '../../hooks/use-copy-text'; import { useCopyText } from '../../hooks/use-copy-text';
import { CopyIcon } from 'lucide-react';
type InviteFriendPopupProps = { type InviteFriendPopupProps = {
befriendUrl: string; befriendUrl: string;
@ -54,11 +54,7 @@ export function InviteFriendPopup(props: InviteFriendPopupProps) {
copyText(befriendUrl); copyText(befriendUrl);
}} }}
> >
<img <CopyIcon className="mr-1 h-4 w-4" />
src={CopyIcon.src}
className="h-4 w-4"
alt="Invite Friends"
/>
{isCopied ? 'Copied' : 'Copy URL'} {isCopied ? 'Copied' : 'Copy URL'}
</button> </button>
</div> </div>

@ -1,10 +1,10 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { httpGet, httpPatch, httpPost } from '../../lib/http'; import { httpGet, httpPatch } from '../../lib/http';
import { pageProgressMessage } from '../../stores/page'; import { pageProgressMessage } from '../../stores/page';
import type { TeamMemberDocument } from '../TeamMembers/TeamMembersPage'; import type { TeamMemberDocument } from '../TeamMembers/TeamMembersPage';
import XIcon from '../../icons/close-dark.svg';
import AcceptIcon from '../../icons/accept.svg';
import { useToast } from '../../hooks/use-toast'; import { useToast } from '../../hooks/use-toast';
import { AcceptIcon } from '../ReactIcons/AcceptIcon.tsx';
import { XIcon } from 'lucide-react';
interface NotificationList extends TeamMemberDocument { interface NotificationList extends TeamMemberDocument {
name: string; name: string;
@ -18,7 +18,7 @@ export function NotificationPage() {
const lostNotifications = async () => { const lostNotifications = async () => {
const { error, response } = await httpGet<NotificationList[]>( const { error, response } = await httpGet<NotificationList[]>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-invitation-list` `${import.meta.env.PUBLIC_API_URL}/v1-get-invitation-list`,
); );
if (error || !response) { if (error || !response) {
toast.error(error?.message || 'Something went wrong'); toast.error(error?.message || 'Something went wrong');
@ -28,28 +28,37 @@ export function NotificationPage() {
setNotifications(response); setNotifications(response);
}; };
async function respondInvitation(status: 'accept' | 'reject', inviteId: string) { async function respondInvitation(
status: 'accept' | 'reject',
inviteId: string,
) {
setIsLoading(true); setIsLoading(true);
setError(''); setError('');
const { response, error } = await httpPatch<{ teamId: string }>( const { response, error } = await httpPatch<{ teamId: string }>(
`${import.meta.env.PUBLIC_API_URL}/v1-respond-invite/${inviteId}`, { `${import.meta.env.PUBLIC_API_URL}/v1-respond-invite/${inviteId}`,
status {
}); status,
},
);
if (error || !response) { if (error || !response) {
setError(error?.message || 'Something went wrong') setError(error?.message || 'Something went wrong');
setIsLoading(false) setIsLoading(false);
return; return;
} }
if (status === 'accept') { if (status === 'accept') {
window.location.href = `/team/progress?t=${response.teamId}`; window.location.href = `/team/progress?t=${response.teamId}`;
} else { } else {
window.dispatchEvent(new CustomEvent('refresh-notification', { window.dispatchEvent(
new CustomEvent('refresh-notification', {
detail: { detail: {
count: notifications.length - 1 count: notifications.length - 1,
} },
})); }),
setNotifications(notifications.filter((notification) => notification._id !== inviteId)); );
setNotifications(
notifications.filter((notification) => notification._id !== inviteId),
);
setIsLoading(false); setIsLoading(false);
} }
} }
@ -66,15 +75,20 @@ export function NotificationPage() {
<h2 className="text-3xl font-bold sm:text-4xl">Notification</h2> <h2 className="text-3xl font-bold sm:text-4xl">Notification</h2>
<p className="mt-2 text-gray-400">Manage your notifications</p> <p className="mt-2 text-gray-400">Manage your notifications</p>
</div> </div>
{ {notifications.length === 0 && (
notifications.length === 0 && ( <div className="mt-6 flex items-center justify-center">
<div className="flex items-center justify-center mt-6">
<p className="text-gray-400"> <p className="text-gray-400">
No notifications, you can <a href="/team/new" className="text-blue-500 underline hover:no-underline">create a team</a> and invite your friends to join. No notifications, you can{' '}
<a
href="/team/new"
className="text-blue-500 underline hover:no-underline"
>
create a team
</a>{' '}
and invite your friends to join.
</p> </p>
</div> </div>
) )}
}
<div className="space-y-4"> <div className="space-y-4">
{notifications.map((notification) => ( {notifications.map((notification) => (
<div className="flex items-center justify-between rounded-md border p-2"> <div className="flex items-center justify-between rounded-md border p-2">
@ -86,19 +100,21 @@ export function NotificationPage() {
</div> </div>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<button type="button" <button
type="button"
disabled={isLoading} disabled={isLoading}
className="inline-flex border p-1 rounded hover:bg-gray-50 disabled:opacity-75" className="inline-flex rounded border p-1 hover:bg-gray-50 disabled:opacity-75"
onClick={() => respondInvitation('accept', notification?._id!)} onClick={() => respondInvitation('accept', notification?._id!)}
> >
<img src={AcceptIcon.src} className="h-4 w-4" /> <AcceptIcon className="h-4 w-4" />
</button> </button>
<button type="button" <button
type="button"
disabled={isLoading} disabled={isLoading}
className="inline-flex border p-1 rounded hover:bg-gray-50 disabled:opacity-75" className="inline-flex rounded border p-1 hover:bg-gray-50 disabled:opacity-75"
onClick={() => respondInvitation('reject', notification?._id!)} onClick={() => respondInvitation('reject', notification?._id!)}
> >
<img alt={'Close'} src={XIcon.src} className="h-4 w-4" /> <XIcon className="h-4 w-4" />
</button> </button>
</div> </div>
</div> </div>

@ -0,0 +1,24 @@
type AcceptIconProps = {
className?: string;
};
export function AcceptIcon(props: AcceptIconProps) {
const { className } = props;
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="2"
stroke="#000"
className={className}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4.5 12.75l6 6 9-13.5"
/>
</svg>
);
}

@ -1,7 +1,5 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { httpGet, httpPatch } from '../lib/http'; import { httpGet, httpPatch } from '../lib/http';
import BuildingIcon from '../icons/building.svg';
import ErrorIcon from '../icons/error.svg';
import { pageProgressMessage } from '../stores/page'; import { pageProgressMessage } from '../stores/page';
import type { TeamDocument } from './CreateTeam/CreateTeamForm'; import type { TeamDocument } from './CreateTeam/CreateTeamForm';
import type { AllowedRoles } from './CreateTeam/RoleDropdown'; import type { AllowedRoles } from './CreateTeam/RoleDropdown';
@ -9,6 +7,8 @@ import type { AllowedMemberStatus } from './TeamDropdown/TeamDropdown';
import { isLoggedIn } from '../lib/jwt'; import { isLoggedIn } from '../lib/jwt';
import { showLoginPopup } from '../lib/popup'; import { showLoginPopup } from '../lib/popup';
import { getUrlParams } from '../lib/browser'; import { getUrlParams } from '../lib/browser';
import { ErrorIcon2 } from './ReactIcons/ErrorIcon2';
import { BuildingIcon } from './ReactIcons/BuildingIcon';
type InvitationResponse = { type InvitationResponse = {
team: TeamDocument; team: TeamDocument;
@ -85,11 +85,7 @@ export function RespondInviteForm() {
if (!invite) { if (!invite) {
return ( return (
<div className="container text-center"> <div className="container text-center">
<img <ErrorIcon2 className="mx-auto mb-4 mt-24 w-20 opacity-20" />
alt={'error'}
src={ErrorIcon.src}
className="mx-auto mb-4 mt-24 w-20 opacity-20"
/>
<h2 className={'mb-1 text-2xl font-bold'}>Error</h2> <h2 className={'mb-1 text-2xl font-bold'}>Error</h2>
<p className="mb-4 text-base leading-6 text-gray-600"> <p className="mb-4 text-base leading-6 text-gray-600">
@ -110,11 +106,7 @@ export function RespondInviteForm() {
return ( return (
<div className="container text-center"> <div className="container text-center">
<img <BuildingIcon className="mx-auto mb-4 mt-24 w-20 opacity-20" />
alt={'join team'}
src={BuildingIcon.src}
className="mx-auto mb-4 mt-24 w-20 opacity-20"
/>
<h2 className={'mb-1 text-2xl font-bold'}>Join Team</h2> <h2 className={'mb-1 text-2xl font-bold'}>Join Team</h2>
<p className="mb-3 text-base leading-6 text-gray-600"> <p className="mb-3 text-base leading-6 text-gray-600">

@ -1,8 +1,8 @@
import { useState } from 'react'; import { useState } from 'react';
import type { GroupByRoadmap, TeamMember } from './TeamProgressPage'; import type { GroupByRoadmap, TeamMember } from './TeamProgressPage';
import { getUrlParams } from '../../lib/browser'; import { getUrlParams } from '../../lib/browser';
import ExternalLinkIcon from '../../icons/external-link.svg';
import { useAuth } from '../../hooks/use-auth'; import { useAuth } from '../../hooks/use-auth';
import { LucideExternalLink } from 'lucide-react';
type GroupRoadmapItemProps = { type GroupRoadmapItemProps = {
roadmap: GroupByRoadmap; roadmap: GroupByRoadmap;
@ -33,11 +33,7 @@ export function GroupRoadmapItem(props: GroupRoadmapItemProps) {
className="group mb-0.5 flex shrink-0 items-center justify-between text-base font-medium leading-none text-black" className="group mb-0.5 flex shrink-0 items-center justify-between text-base font-medium leading-none text-black"
target={'_blank'} target={'_blank'}
> >
<img <LucideExternalLink className="h-4 w-4 opacity-20 transition-opacity group-hover:opacity-100" />
alt={'link'}
src={ExternalLinkIcon.src}
className="ml-2 h-4 w-4 opacity-20 transition-opacity group-hover:opacity-100"
/>
</a> </a>
</div> </div>
</div> </div>
@ -58,7 +54,7 @@ export function GroupRoadmapItem(props: GroupRoadmapItemProps) {
onClick={() => { onClick={() => {
onShowResourceProgress( onShowResourceProgress(
member.member, member.member,
member.progress?.resourceId! member.progress?.resourceId!,
); );
}} }}
> >

@ -25,6 +25,7 @@ import type { Node } from 'reactflow';
import { useKeydown } from '../../hooks/use-keydown'; import { useKeydown } from '../../hooks/use-keydown';
import { useOutsideClick } from '../../hooks/use-outside-click'; import { useOutsideClick } from '../../hooks/use-outside-click';
import { MemberProgressModalHeader } from './MemberProgressModalHeader'; import { MemberProgressModalHeader } from './MemberProgressModalHeader';
import { X } from 'lucide-react';
export type ProgressMapProps = { export type ProgressMapProps = {
member: TeamMember; member: TeamMember;
@ -284,7 +285,7 @@ export function MemberCustomProgressModal(props: ProgressMapProps) {
}`} }`}
onClick={onClose} onClick={onClose}
> >
<img alt={'close'} src={CloseIcon.src} className="h-4 w-4" /> <X className="h-4 w-4" />
<span className="sr-only">Close modal</span> <span className="sr-only">Close modal</span>
</button> </button>
</div> </div>

@ -12,12 +12,12 @@ import {
type ResourceType, type ResourceType,
updateResourceProgress, updateResourceProgress,
} from '../../lib/resource-progress'; } from '../../lib/resource-progress';
import CloseIcon from '../../icons/close.svg';
import { useToast } from '../../hooks/use-toast'; import { useToast } from '../../hooks/use-toast';
import { useAuth } from '../../hooks/use-auth'; import { useAuth } from '../../hooks/use-auth';
import { pageProgressMessage } from '../../stores/page'; import { pageProgressMessage } from '../../stores/page';
import { MemberProgressModalHeader } from './MemberProgressModalHeader'; import { MemberProgressModalHeader } from './MemberProgressModalHeader';
import {replaceChildren} from "../../lib/dom.ts"; import { replaceChildren } from '../../lib/dom.ts';
import { XIcon } from 'lucide-react';
export type ProgressMapProps = { export type ProgressMapProps = {
member: TeamMember; member: TeamMember;
@ -68,12 +68,12 @@ export function MemberProgressModal(props: ProgressMapProps) {
teamId: string, teamId: string,
memberId: string, memberId: string,
resourceType: string, resourceType: string,
resourceId: string resourceId: string,
) { ) {
const { error, response } = await httpGet<MemberProgressResponse>( const { error, response } = await httpGet<MemberProgressResponse>(
`${ `${
import.meta.env.PUBLIC_API_URL import.meta.env.PUBLIC_API_URL
}/v1-get-member-resource-progress/${teamId}/${memberId}?resourceType=${resourceType}&resourceId=${resourceId}` }/v1-get-member-resource-progress/${teamId}/${memberId}?resourceType=${resourceType}&resourceId=${resourceId}`,
); );
if (error || !response) { if (error || !response) {
toast.error(error?.message || 'Failed to get member progress'); toast.error(error?.message || 'Failed to get member progress');
@ -160,14 +160,14 @@ export function MemberProgressModal(props: ProgressMapProps) {
resourceType: resourceType as ResourceType, resourceType: resourceType as ResourceType,
topicId, topicId,
}, },
newStatus newStatus,
) )
.then(() => { .then(() => {
renderTopicProgress(topicId, newStatus); renderTopicProgress(topicId, newStatus);
getMemberProgress(teamId, member._id, resourceType, resourceId).then( getMemberProgress(teamId, member._id, resourceType, resourceId).then(
(data) => { (data) => {
setMemberProgress(data); setMemberProgress(data);
} },
); );
}) })
.catch((err) => { .catch((err) => {
@ -227,7 +227,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
e.preventDefault(); e.preventDefault();
updateTopicStatus( updateTopicStatus(
topicId, topicId,
!isCurrentStatusLearning ? 'learning' : 'pending' !isCurrentStatusLearning ? 'learning' : 'pending',
); );
return; return;
} }
@ -236,7 +236,7 @@ export function MemberProgressModal(props: ProgressMapProps) {
e.preventDefault(); e.preventDefault();
updateTopicStatus( updateTopicStatus(
topicId, topicId,
!isCurrentStatusSkipped ? 'skipped' : 'pending' !isCurrentStatusSkipped ? 'skipped' : 'pending',
); );
return; return;
@ -298,7 +298,8 @@ export function MemberProgressModal(props: ProgressMapProps) {
}`} }`}
onClick={onClose} onClick={onClose}
> >
<img alt={'close'} src={CloseIcon.src} className="h-4 w-4" /> <XIcon className="h-4 w-4" />
<span className="sr-only">Close modal</span> <span className="sr-only">Close modal</span>
</button> </button>
</div> </div>

@ -4,7 +4,6 @@ import type { TeamDocument } from '../CreateTeam/CreateTeamForm';
import type { TeamResourceConfig } from '../CreateTeam/RoadmapSelector'; import type { TeamResourceConfig } from '../CreateTeam/RoadmapSelector';
import { httpGet, httpPut } from '../../lib/http'; import { httpGet, httpPut } from '../../lib/http';
import { pageProgressMessage } from '../../stores/page'; import { pageProgressMessage } from '../../stores/page';
import RoadmapIcon from '../../icons/roadmap.svg';
import type { PageType } from '../CommandMenu/CommandMenu'; import type { PageType } from '../CommandMenu/CommandMenu';
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { $canManageCurrentTeam } from '../../stores/team'; import { $canManageCurrentTeam } from '../../stores/team';
@ -28,6 +27,7 @@ import { RoadmapActionDropdown } from './RoadmapActionDropdown';
import { UpdateTeamResourceModal } from '../CreateTeam/UpdateTeamResourceModal'; import { UpdateTeamResourceModal } from '../CreateTeam/UpdateTeamResourceModal';
import { ShareOptionsModal } from '../ShareOptions/ShareOptionsModal'; import { ShareOptionsModal } from '../ShareOptions/ShareOptionsModal';
import { cn } from '../../lib/classname'; import { cn } from '../../lib/classname';
import { RoadmapIcon } from '../ReactIcons/RoadmapIcon.tsx';
export function TeamRoadmaps() { export function TeamRoadmaps() {
const { t: teamId } = getUrlParams(); const { t: teamId } = getUrlParams();
@ -73,7 +73,7 @@ export function TeamRoadmaps() {
async function loadTeam(teamIdToFetch: string) { async function loadTeam(teamIdToFetch: string) {
const { response, error } = await httpGet<TeamDocument>( const { response, error } = await httpGet<TeamDocument>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-team/${teamIdToFetch}` `${import.meta.env.PUBLIC_API_URL}/v1-get-team/${teamIdToFetch}`,
); );
if (error || !response) { if (error || !response) {
@ -87,7 +87,7 @@ export function TeamRoadmaps() {
async function loadTeamResourceConfig(teamId: string) { async function loadTeamResourceConfig(teamId: string) {
const { error, response } = await httpGet<TeamResourceConfig>( const { error, response } = await httpGet<TeamResourceConfig>(
`${import.meta.env.PUBLIC_API_URL}/v1-get-team-resource-config/${teamId}` `${import.meta.env.PUBLIC_API_URL}/v1-get-team-resource-config/${teamId}`,
); );
if (error || !Array.isArray(response)) { if (error || !Array.isArray(response)) {
console.error(error); console.error(error);
@ -127,7 +127,7 @@ export function TeamRoadmaps() {
{ {
resourceId: roadmapId, resourceId: roadmapId,
resourceType: 'roadmap', resourceType: 'roadmap',
} },
); );
if (error || !response) { if (error || !response) {
@ -156,7 +156,7 @@ export function TeamRoadmaps() {
resourceId: roadmapId, resourceId: roadmapId,
resourceType: 'roadmap', resourceType: 'roadmap',
removed: [], removed: [],
} },
); );
if (error || !response) { if (error || !response) {
@ -190,13 +190,13 @@ export function TeamRoadmaps() {
} }
window.addEventListener( window.addEventListener(
'custom-roadmap-created', 'custom-roadmap-created',
handleCustomRoadmapCreated handleCustomRoadmapCreated,
); );
return () => { return () => {
window.removeEventListener( window.removeEventListener(
'custom-roadmap-created', 'custom-roadmap-created',
handleCustomRoadmapCreated handleCustomRoadmapCreated,
); );
}; };
}, []); }, []);
@ -252,13 +252,13 @@ export function TeamRoadmaps() {
); );
const placeholderRoadmaps = teamResources.filter( const placeholderRoadmaps = teamResources.filter(
(c: TeamResourceConfig[0]) => c.isCustomResource && !c.topics (c: TeamResourceConfig[0]) => c.isCustomResource && !c.topics,
); );
const customRoadmaps = teamResources.filter( const customRoadmaps = teamResources.filter(
(c: TeamResourceConfig[0]) => c.isCustomResource && c.topics (c: TeamResourceConfig[0]) => c.isCustomResource && c.topics,
); );
const defaultRoadmaps = teamResources.filter( const defaultRoadmaps = teamResources.filter(
(c: TeamResourceConfig[0]) => !c.isCustomResource (c: TeamResourceConfig[0]) => !c.isCustomResource,
); );
const hasRoadmaps = const hasRoadmaps =
@ -272,11 +272,8 @@ export function TeamRoadmaps() {
{addRoadmapModal} {addRoadmapModal}
{createRoadmapModal} {createRoadmapModal}
<img <RoadmapIcon className="mb-4 h-24 w-24 opacity-10" />
alt="roadmap"
src={RoadmapIcon.src}
className="mb-4 h-24 w-24 opacity-10"
/>
<h3 className="mb-1 text-2xl font-bold text-gray-900">No roadmaps</h3> <h3 className="mb-1 text-2xl font-bold text-gray-900">No roadmaps</h3>
<p className="text-base text-gray-500"> <p className="text-base text-gray-500">
{canManageCurrentTeam {canManageCurrentTeam
@ -380,11 +377,11 @@ export function TeamRoadmaps() {
onDelete={() => { onDelete={() => {
if ( if (
confirm( confirm(
'Are you sure you want to remove this roadmap?' 'Are you sure you want to remove this roadmap?',
) )
) { ) {
onRemove(resourceConfig.resourceId).finally( onRemove(resourceConfig.resourceId).finally(
() => {} () => {},
); );
} }
}} }}
@ -405,7 +402,7 @@ export function TeamRoadmaps() {
)} )}
</div> </div>
); );
} },
)} )}
</div> </div>
</div> </div>
@ -433,7 +430,7 @@ export function TeamRoadmaps() {
'grid grid-cols-1 p-2.5', 'grid grid-cols-1 p-2.5',
canManageCurrentTeam canManageCurrentTeam
? 'sm:grid-cols-[auto_172px]' ? 'sm:grid-cols-[auto_172px]'
: 'sm:grid-cols-[auto_110px]' : 'sm:grid-cols-[auto_110px]',
)} )}
key={resourceConfig.resourceId} key={resourceConfig.resourceId}
> >
@ -464,11 +461,11 @@ export function TeamRoadmaps() {
onDelete={() => { onDelete={() => {
if ( if (
confirm( confirm(
'Are you sure you want to remove this roadmap?' 'Are you sure you want to remove this roadmap?',
) )
) { ) {
onRemove(resourceConfig.resourceId).finally( onRemove(resourceConfig.resourceId).finally(
() => {} () => {},
); );
} }
}} }}
@ -557,11 +554,11 @@ export function TeamRoadmaps() {
onDelete={() => { onDelete={() => {
if ( if (
confirm( confirm(
'Are you sure you want to remove this roadmap?' 'Are you sure you want to remove this roadmap?',
) )
) { ) {
onRemove(resourceConfig.resourceId).finally( onRemove(resourceConfig.resourceId).finally(
() => {} () => {},
); );
} }
}} }}

@ -4,13 +4,13 @@ import { useKeydown } from '../../hooks/use-keydown';
import { httpGet } from '../../lib/http'; import { httpGet } from '../../lib/http';
import type { ResourceType } from '../../lib/resource-progress'; import type { ResourceType } from '../../lib/resource-progress';
import { topicSelectorAll } from '../../lib/resource-progress'; import { topicSelectorAll } from '../../lib/resource-progress';
import CloseIcon from '../../icons/close.svg';
import { deleteUrlParam, getUrlParams } from '../../lib/browser'; import { deleteUrlParam, getUrlParams } from '../../lib/browser';
import { useAuth } from '../../hooks/use-auth'; import { useAuth } from '../../hooks/use-auth';
import type { GetRoadmapResponse } from '../CustomRoadmap/CustomRoadmap'; import type { GetRoadmapResponse } from '../CustomRoadmap/CustomRoadmap';
import { ReadonlyEditor } from '../../../editor/readonly-editor'; import { ReadonlyEditor } from '../../../editor/readonly-editor';
import { ProgressLoadingError } from './ProgressLoadingError'; import { ProgressLoadingError } from './ProgressLoadingError';
import { UserProgressModalHeader } from './UserProgressModalHeader'; import { UserProgressModalHeader } from './UserProgressModalHeader';
import { X } from 'lucide-react';
export type ProgressMapProps = { export type ProgressMapProps = {
userId?: string; userId?: string;
@ -208,7 +208,7 @@ export function UserCustomProgressModal(props: ProgressMapProps) {
className={`absolute right-2.5 top-3 ml-auto inline-flex items-center rounded-lg bg-gray-100 bg-transparent p-1.5 text-sm text-gray-400 hover:text-gray-900 lg:hidden`} className={`absolute right-2.5 top-3 ml-auto inline-flex items-center rounded-lg bg-gray-100 bg-transparent p-1.5 text-sm text-gray-400 hover:text-gray-900 lg:hidden`}
onClick={onClose} onClick={onClose}
> >
<img alt={'close'} src={CloseIcon.src} className="h-4 w-4" /> <X className="h-4 w-4" />
<span className="sr-only">Close modal</span> <span className="sr-only">Close modal</span>
</button> </button>
</div> </div>

@ -6,11 +6,11 @@ import { useKeydown } from '../../hooks/use-keydown';
import { httpGet } from '../../lib/http'; import { httpGet } from '../../lib/http';
import type { ResourceType } from '../../lib/resource-progress'; import type { ResourceType } from '../../lib/resource-progress';
import { topicSelectorAll } from '../../lib/resource-progress'; import { topicSelectorAll } from '../../lib/resource-progress';
import CloseIcon from '../../icons/close.svg';
import { deleteUrlParam, getUrlParams } from '../../lib/browser'; import { deleteUrlParam, getUrlParams } from '../../lib/browser';
import { useAuth } from '../../hooks/use-auth'; import { useAuth } from '../../hooks/use-auth';
import { ProgressLoadingError } from './ProgressLoadingError'; import { ProgressLoadingError } from './ProgressLoadingError';
import { UserProgressModalHeader } from './UserProgressModalHeader'; import { UserProgressModalHeader } from './UserProgressModalHeader';
import { X } from 'lucide-react';
export type ProgressMapProps = { export type ProgressMapProps = {
userId?: string; userId?: string;
@ -70,12 +70,12 @@ export function UserProgressModal(props: ProgressMapProps) {
async function getUserProgress( async function getUserProgress(
userId: string, userId: string,
resourceType: string, resourceType: string,
resourceId: string resourceId: string,
): Promise<UserProgressResponse | undefined> { ): Promise<UserProgressResponse | undefined> {
const { error, response } = await httpGet<UserProgressResponse>( const { error, response } = await httpGet<UserProgressResponse>(
`${ `${
import.meta.env.PUBLIC_API_URL import.meta.env.PUBLIC_API_URL
}/v1-get-user-progress/${userId}?resourceType=${resourceType}&resourceId=${resourceId}` }/v1-get-user-progress/${userId}?resourceType=${resourceType}&resourceId=${resourceId}`,
); );
if (error || !response) { if (error || !response) {
@ -86,7 +86,7 @@ export function UserProgressModal(props: ProgressMapProps) {
} }
async function getRoadmapSVG( async function getRoadmapSVG(
jsonUrl: string jsonUrl: string,
): Promise<SVGElement | undefined> { ): Promise<SVGElement | undefined> {
const { error, response: roadmapJson } = await httpGet(jsonUrl); const { error, response: roadmapJson } = await httpGet(jsonUrl);
if (error || !roadmapJson) { if (error || !roadmapJson) {
@ -216,7 +216,7 @@ export function UserProgressModal(props: ProgressMapProps) {
className={`absolute right-2.5 top-3 ml-auto inline-flex items-center rounded-lg bg-gray-100 bg-transparent p-1.5 text-sm text-gray-400 hover:text-gray-900 lg:hidden`} className={`absolute right-2.5 top-3 ml-auto inline-flex items-center rounded-lg bg-gray-100 bg-transparent p-1.5 text-sm text-gray-400 hover:text-gray-900 lg:hidden`}
onClick={onClose} onClick={onClose}
> >
<img alt={'close'} src={CloseIcon.src} className="h-4 w-4" /> <X className="h-4 w-4" />
<span className="sr-only">Close modal</span> <span className="sr-only">Close modal</span>
</button> </button>
</div> </div>

Loading…
Cancel
Save