mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-03 06:12:53 +02:00
Minor Improvement for Custom Roadmap (#4590)
* Add Edit button in the roadmap list * Add share with others button * Fix editor link
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
Globe,
|
Globe,
|
||||||
LockIcon,
|
LockIcon,
|
||||||
Users,
|
Users,
|
||||||
|
PenSquare,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useToast } from '../../hooks/use-toast';
|
import { useToast } from '../../hooks/use-toast';
|
||||||
import {
|
import {
|
||||||
@@ -142,7 +143,7 @@ function CustomRoadmapItem(props: CustomRoadmapItemProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className="grid grid-cols-1 p-2.5 sm:grid-cols-[auto_110px]"
|
className="grid grid-cols-1 p-2.5 sm:grid-cols-[auto_172px]"
|
||||||
key={roadmap._id!}
|
key={roadmap._id!}
|
||||||
>
|
>
|
||||||
<div className="mb-3 grid grid-cols-1 sm:mb-0">
|
<div className="mb-3 grid grid-cols-1 sm:mb-0">
|
||||||
@@ -184,6 +185,16 @@ function CustomRoadmapItem(props: CustomRoadmapItemProps) {
|
|||||||
<ExternalLink className="inline-block h-4 w-4" />
|
<ExternalLink className="inline-block h-4 w-4" />
|
||||||
Visit
|
Visit
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
href={editorLink}
|
||||||
|
className={
|
||||||
|
'ml-2 flex items-center gap-2 rounded-md border border-gray-800 bg-gray-900 px-2.5 py-1.5 text-xs text-white hover:bg-gray-800 focus:outline-none'
|
||||||
|
}
|
||||||
|
target={'_blank'}
|
||||||
|
>
|
||||||
|
<PenSquare className="inline-block h-4 w-4" />
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
@@ -9,6 +9,8 @@ import { type TeamResourceConfig } from '../CreateTeam/RoadmapSelector';
|
|||||||
import { useToast } from '../../hooks/use-toast';
|
import { useToast } from '../../hooks/use-toast';
|
||||||
import { RoadmapActionButton } from './RoadmapActionButton';
|
import { RoadmapActionButton } from './RoadmapActionButton';
|
||||||
import { Lock, Shapes } from 'lucide-react';
|
import { Lock, Shapes } from 'lucide-react';
|
||||||
|
import { Modal } from '../Modal';
|
||||||
|
import { ShareSuccess } from '../ShareOptions/ShareSuccess';
|
||||||
|
|
||||||
type RoadmapHeaderProps = {};
|
type RoadmapHeaderProps = {};
|
||||||
|
|
||||||
@@ -22,9 +24,11 @@ export function RoadmapHeader(props: RoadmapHeaderProps) {
|
|||||||
_id: roadmapId,
|
_id: roadmapId,
|
||||||
creator,
|
creator,
|
||||||
team,
|
team,
|
||||||
|
visibility,
|
||||||
} = useStore(currentRoadmap) || {};
|
} = useStore(currentRoadmap) || {};
|
||||||
|
|
||||||
const [isSharing, setIsSharing] = useState(false);
|
const [isSharing, setIsSharing] = useState(false);
|
||||||
|
const [isSharingWithOthers, setIsSharingWithOthers] = useState(false);
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
async function deleteResource() {
|
async function deleteResource() {
|
||||||
@@ -65,6 +69,22 @@ export function RoadmapHeader(props: RoadmapHeaderProps) {
|
|||||||
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${creator?.avatar}`
|
? `${import.meta.env.PUBLIC_AVATAR_BASE_URL}/${creator?.avatar}`
|
||||||
: '/images/default-avatar.png';
|
: '/images/default-avatar.png';
|
||||||
|
|
||||||
|
const sharingWithOthersModal = isSharingWithOthers && (
|
||||||
|
<Modal
|
||||||
|
onClose={() => setIsSharingWithOthers(false)}
|
||||||
|
wrapperClassName="max-w-lg"
|
||||||
|
bodyClassName="p-4 flex flex-col"
|
||||||
|
>
|
||||||
|
<ShareSuccess
|
||||||
|
visibility="public"
|
||||||
|
roadmapId={roadmapId!}
|
||||||
|
description={description}
|
||||||
|
onClose={() => setIsSharingWithOthers(false)}
|
||||||
|
isSharingWithOthers={true}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-b">
|
<div className="border-b">
|
||||||
<div className="container relative py-5 sm:py-12">
|
<div className="container relative py-5 sm:py-12">
|
||||||
@@ -117,63 +137,78 @@ export function RoadmapHeader(props: RoadmapHeaderProps) {
|
|||||||
<span className="ml-2">Subscribe</span>
|
<span className="ml-2">Subscribe</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{$canManageCurrentRoadmap && (
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center gap-2">
|
{$canManageCurrentRoadmap && (
|
||||||
{isSharing && $currentRoadmap && (
|
<>
|
||||||
<ShareOptionsModal
|
{isSharing && $currentRoadmap && (
|
||||||
isDiscoverable={$currentRoadmap.isDiscoverable}
|
<ShareOptionsModal
|
||||||
description={$currentRoadmap?.description}
|
isDiscoverable={$currentRoadmap.isDiscoverable}
|
||||||
visibility={$currentRoadmap?.visibility}
|
description={$currentRoadmap?.description}
|
||||||
teamId={$currentRoadmap?.teamId}
|
visibility={$currentRoadmap?.visibility}
|
||||||
roadmapId={$currentRoadmap?._id!}
|
teamId={$currentRoadmap?.teamId}
|
||||||
sharedFriendIds={$currentRoadmap?.sharedFriendIds || []}
|
roadmapId={$currentRoadmap?._id!}
|
||||||
sharedTeamMemberIds={
|
sharedFriendIds={$currentRoadmap?.sharedFriendIds || []}
|
||||||
$currentRoadmap?.sharedTeamMemberIds || []
|
sharedTeamMemberIds={
|
||||||
}
|
$currentRoadmap?.sharedTeamMemberIds || []
|
||||||
onClose={() => setIsSharing(false)}
|
}
|
||||||
onShareSettingsUpdate={(settings) => {
|
onClose={() => setIsSharing(false)}
|
||||||
currentRoadmap.set({
|
onShareSettingsUpdate={(settings) => {
|
||||||
...$currentRoadmap,
|
currentRoadmap.set({
|
||||||
...settings,
|
...$currentRoadmap,
|
||||||
});
|
...settings,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<a
|
||||||
|
href={`${import.meta.env.PUBLIC_EDITOR_APP_URL}/${
|
||||||
|
$currentRoadmap?._id
|
||||||
|
}`}
|
||||||
|
target="_blank"
|
||||||
|
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white py-1.5 pl-2 pr-2 text-xs font-medium text-black hover:border-gray-300 hover:bg-gray-300 sm:px-3 sm:text-sm"
|
||||||
|
>
|
||||||
|
<Shapes className="mr-1.5 h-4 w-4 stroke-[2.5]" />
|
||||||
|
<span className="hidden sm:inline-block">Edit Roadmap</span>
|
||||||
|
<span className="sm:hidden">Edit</span>
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsSharing(true)}
|
||||||
|
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white py-1.5 pl-2 pr-2 text-xs font-medium text-black hover:border-gray-300 hover:bg-gray-300 sm:px-3 sm:text-sm"
|
||||||
|
>
|
||||||
|
<Lock className="mr-1.5 h-4 w-4 stroke-[2.5]" />
|
||||||
|
Sharing
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<RoadmapActionButton
|
||||||
|
onDelete={() => {
|
||||||
|
const confirmation = window.confirm(
|
||||||
|
'Are you sure you want to delete this roadmap?'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!confirmation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteResource().finally(() => null);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<a
|
{!$canManageCurrentRoadmap && visibility === 'public' && (
|
||||||
href={`${import.meta.env.PUBLIC_EDITOR_APP_URL}/${
|
<>
|
||||||
$currentRoadmap?._id
|
{sharingWithOthersModal}
|
||||||
}`}
|
<button
|
||||||
target="_blank"
|
onClick={() => setIsSharingWithOthers(true)}
|
||||||
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white py-1.5 pl-2 pr-2 text-xs font-medium text-black hover:border-gray-300 hover:bg-gray-300 sm:px-3 sm:text-sm"
|
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white py-1.5 pl-2 pr-2 text-xs font-medium text-black hover:border-gray-300 hover:bg-gray-300 sm:px-3 sm:text-sm"
|
||||||
>
|
>
|
||||||
<Shapes className="mr-1.5 h-4 w-4 stroke-[2.5]" />
|
<Lock className="mr-1.5 h-4 w-4 stroke-[2.5]" />
|
||||||
<span className="hidden sm:inline-block">Edit Roadmap</span>
|
Share with Others
|
||||||
<span className="sm:hidden">Edit</span>
|
</button>
|
||||||
</a>
|
</>
|
||||||
<button
|
)}
|
||||||
onClick={() => setIsSharing(true)}
|
</div>
|
||||||
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white py-1.5 pl-2 pr-2 text-xs font-medium text-black hover:border-gray-300 hover:bg-gray-300 sm:px-3 sm:text-sm"
|
|
||||||
>
|
|
||||||
<Lock className="mr-1.5 h-4 w-4 stroke-[2.5]" />
|
|
||||||
Sharing
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<RoadmapActionButton
|
|
||||||
onDelete={() => {
|
|
||||||
const confirmation = window.confirm(
|
|
||||||
'Are you sure you want to delete this roadmap?'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!confirmation) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteResource().finally(() => null);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<RoadmapHint
|
<RoadmapHint
|
||||||
|
@@ -8,10 +8,17 @@ type ShareSuccessProps = {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
visibility: AllowedRoadmapVisibility;
|
visibility: AllowedRoadmapVisibility;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
isSharingWithOthers?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ShareSuccess(props: ShareSuccessProps) {
|
export function ShareSuccess(props: ShareSuccessProps) {
|
||||||
const { roadmapId, onClose, description, visibility } = props;
|
const {
|
||||||
|
roadmapId,
|
||||||
|
onClose,
|
||||||
|
description,
|
||||||
|
visibility,
|
||||||
|
isSharingWithOthers = false,
|
||||||
|
} = props;
|
||||||
|
|
||||||
const baseUrl = import.meta.env.DEV
|
const baseUrl = import.meta.env.DEV
|
||||||
? 'http://localhost:3000'
|
? 'http://localhost:3000'
|
||||||
@@ -42,7 +49,11 @@ export function ShareSuccess(props: ShareSuccessProps) {
|
|||||||
<div className="flex grow flex-col justify-center">
|
<div className="flex grow flex-col justify-center">
|
||||||
<div className="mt-5 flex grow flex-col items-center justify-center gap-1.5">
|
<div className="mt-5 flex grow flex-col items-center justify-center gap-1.5">
|
||||||
<CheckCircle className="h-14 w-14 text-green-500" />
|
<CheckCircle className="h-14 w-14 text-green-500" />
|
||||||
<h3 className="text-xl font-medium">Sharing Settings Updated</h3>
|
{isSharingWithOthers ? (
|
||||||
|
<h3 className="text-xl font-medium">Sharing with Others</h3>
|
||||||
|
) : (
|
||||||
|
<h3 className="text-xl font-medium">Sharing Settings Updated</h3>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@@ -55,15 +66,21 @@ export function ShareSuccess(props: ShareSuccessProps) {
|
|||||||
copyText(shareLink);
|
copyText(shareLink);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<p className="mt-1 text-sm text-gray-400">
|
{isSharingWithOthers ? (
|
||||||
You can share the above link with anyone who has access
|
<p className="mt-1 text-sm text-gray-400">
|
||||||
</p>
|
You can share the above link with anyone
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p className="mt-1 text-sm text-gray-400">
|
||||||
|
You can share the above link with anyone who has access
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
{visibility === 'public' && (
|
{visibility === 'public' && (
|
||||||
<>
|
<>
|
||||||
<div className="-mx-4 mt-4 flex items-center gap-1.5">
|
<div className="-mx-4 mt-4 flex items-center gap-1.5">
|
||||||
<span className="h-px grow bg-gray-300" />
|
<span className="h-px grow bg-gray-300" />
|
||||||
<span className="text-xs uppercase text-gray-400 px-2">Or</span>
|
<span className="px-2 text-xs uppercase text-gray-400">Or</span>
|
||||||
<span className="h-px grow bg-gray-300" />
|
<span className="h-px grow bg-gray-300" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -27,6 +27,7 @@ import {
|
|||||||
import { RoadmapActionDropdown } from './RoadmapActionDropdown';
|
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';
|
||||||
|
|
||||||
export function TeamRoadmaps() {
|
export function TeamRoadmaps() {
|
||||||
const { t: teamId } = getUrlParams();
|
const { t: teamId } = getUrlParams();
|
||||||
@@ -428,7 +429,12 @@ export function TeamRoadmaps() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="grid grid-cols-1 p-2.5 sm:grid-cols-[auto_110px]"
|
className={cn(
|
||||||
|
'grid grid-cols-1 p-2.5',
|
||||||
|
canManageCurrentTeam
|
||||||
|
? 'sm:grid-cols-[auto_172px]'
|
||||||
|
: 'sm:grid-cols-[auto_110px]'
|
||||||
|
)}
|
||||||
key={resourceConfig.resourceId}
|
key={resourceConfig.resourceId}
|
||||||
>
|
>
|
||||||
<div className="mb-3 grid grid-cols-1 sm:mb-0">
|
<div className="mb-3 grid grid-cols-1 sm:mb-0">
|
||||||
@@ -479,6 +485,18 @@ export function TeamRoadmaps() {
|
|||||||
<ExternalLink className="inline-block h-4 w-4" />
|
<ExternalLink className="inline-block h-4 w-4" />
|
||||||
Visit
|
Visit
|
||||||
</a>
|
</a>
|
||||||
|
{canManageCurrentTeam && (
|
||||||
|
<a
|
||||||
|
href={editorLink}
|
||||||
|
className={
|
||||||
|
'ml-2 flex items-center gap-2 rounded-md border border-gray-800 bg-gray-900 px-2.5 py-1.5 text-xs text-white hover:bg-gray-800 focus:outline-none'
|
||||||
|
}
|
||||||
|
target={'_blank'}
|
||||||
|
>
|
||||||
|
<PenSquare className="inline-block h-4 w-4" />
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user