Use the same add roadmap modal

chore/update-progress^2
Kamran Ahmed 1 year ago
parent 5bc33cb527
commit 36cd03f14f
  1. 26
      src/components/CreateTeam/RoadmapSelector.tsx
  2. 15
      src/components/CreateTeam/Step2.tsx
  3. 4
      src/components/TeamDropdown/TeamDropdown.tsx
  4. 108
      src/components/TeamRoadmaps.tsx
  5. 2
      src/components/Toast.tsx

@ -14,13 +14,13 @@ export type TeamResourceConfig = {
}[]; }[];
type RoadmapSelectorProps = { type RoadmapSelectorProps = {
team: TeamDocument; teamId: string;
teamResourceConfig: TeamResourceConfig; teamResourceConfig: TeamResourceConfig;
setTeamResourceConfig: (config: TeamResourceConfig) => void; setTeamResourceConfig: (config: TeamResourceConfig) => void;
}; };
export function RoadmapSelector(props: RoadmapSelectorProps) { export function RoadmapSelector(props: RoadmapSelectorProps) {
const { team, teamResourceConfig = [], setTeamResourceConfig } = props; const { teamId, teamResourceConfig = [], setTeamResourceConfig } = props;
const [showSelectRoadmapModal, setShowSelectRoadmapModal] = useState(false); const [showSelectRoadmapModal, setShowSelectRoadmapModal] = useState(false);
const [allRoadmaps, setAllRoadmaps] = useState<PageType[]>([]); const [allRoadmaps, setAllRoadmaps] = useState<PageType[]>([]);
@ -51,15 +51,15 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
} }
async function deleteResource(roadmapId: string) { async function deleteResource(roadmapId: string) {
if (!team?._id) { if (!teamId) {
return; return;
} }
pageProgressMessage.set(`Deleting resource`); pageProgressMessage.set(`Deleting resource`);
const { error, response } = await httpPut<TeamResourceConfig>( const { error, response } = await httpPut<TeamResourceConfig>(
`${import.meta.env.PUBLIC_API_URL}/v1-delete-team-resource-config/${ `${
team._id import.meta.env.PUBLIC_API_URL
}`, }/v1-delete-team-resource-config/${teamId}`,
{ {
resourceId: roadmapId, resourceId: roadmapId,
resourceType: 'roadmap', resourceType: 'roadmap',
@ -83,17 +83,17 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
} }
async function addTeamResource(roadmapId: string) { async function addTeamResource(roadmapId: string) {
if (!team?._id) { if (!teamId) {
return; return;
} }
pageProgressMessage.set(`Adding roadmap to team`); pageProgressMessage.set(`Adding roadmap to team`);
const { error, response } = await httpPut<TeamResourceConfig>( const { error, response } = await httpPut<TeamResourceConfig>(
`${import.meta.env.PUBLIC_API_URL}/v1-update-team-resource-config/${ `${
team._id import.meta.env.PUBLIC_API_URL
}`, }/v1-update-team-resource-config/${teamId}`,
{ {
teamId: team._id, teamId: teamId,
resourceId: roadmapId, resourceId: roadmapId,
resourceType: 'roadmap', resourceType: 'roadmap',
removed: [], removed: [],
@ -119,7 +119,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
onClose={() => setChangingRoadmapId('')} onClose={() => setChangingRoadmapId('')}
resourceId={changingRoadmapId} resourceId={changingRoadmapId}
resourceType={'roadmap'} resourceType={'roadmap'}
teamId={team?._id!} teamId={teamId}
setTeamResourceConfig={setTeamResourceConfig} setTeamResourceConfig={setTeamResourceConfig}
defaultRemovedItems={ defaultRemovedItems={
teamResourceConfig.find((c) => c.resourceId === changingRoadmapId) teamResourceConfig.find((c) => c.resourceId === changingRoadmapId)
@ -132,7 +132,7 @@ export function RoadmapSelector(props: RoadmapSelectorProps) {
onClose={() => setShowSelectRoadmapModal(false)} onClose={() => setShowSelectRoadmapModal(false)}
teamResourceConfig={teamResourceConfig} teamResourceConfig={teamResourceConfig}
allRoadmaps={allRoadmaps} allRoadmaps={allRoadmaps}
teamId={team?._id!} teamId={teamId}
onRoadmapAdd={(roadmapId) => { onRoadmapAdd={(roadmapId) => {
addTeamResource(roadmapId).finally(() => { addTeamResource(roadmapId).finally(() => {
pageProgressMessage.set(''); pageProgressMessage.set('');

@ -24,7 +24,7 @@ export function Step2(props: Step2Props) {
</div> </div>
<RoadmapSelector <RoadmapSelector
team={team} teamId={team._id!}
teamResourceConfig={teamResourceConfig} teamResourceConfig={teamResourceConfig}
setTeamResourceConfig={setTeamResourceConfig} setTeamResourceConfig={setTeamResourceConfig}
/> />
@ -41,18 +41,6 @@ export function Step2(props: Step2Props) {
<span className="mr-1">&larr;</span> <span className="mr-1">&larr;</span>
Previous Step Previous Step
</button> </button>
<div className={'flex gap-2'}>
{teamResourceConfig.length === 0 && (
<button
type="button"
onClick={onNext}
className={
'rounded-md border border-gray-300 bg-white px-4 py-2 text-gray-500 hover:border-gray-400 hover:text-black'
}
>
Skip for Now
</button>
)}
<button <button
type="submit" type="submit"
disabled={teamResourceConfig.length === 0} disabled={teamResourceConfig.length === 0}
@ -65,7 +53,6 @@ export function Step2(props: Step2Props) {
<span className="ml-1">&rarr;</span> <span className="ml-1">&rarr;</span>
</button> </button>
</div> </div>
</div>
</> </>
); );
} }

@ -139,10 +139,6 @@ export function TeamDropdown() {
pageLink = `/team/progress?t=${team._id}`; pageLink = `/team/progress?t=${team._id}`;
} }
if (team.roadmaps.length === 0) {
pageLink = `/team/new?t=${team._id}&s=2`;
}
return ( return (
<li> <li>
<a <a

@ -5,13 +5,14 @@ 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 ExternalLinkIcon from '../icons/external-link.svg'; import ExternalLinkIcon from '../icons/external-link.svg';
import RoadmapIcon from '../icons/roadmap.svg';
import PlusIcon from '../icons/plus.svg'; import PlusIcon from '../icons/plus.svg';
import type { PageType } from './CommandMenu/CommandMenu'; import type { PageType } from './CommandMenu/CommandMenu';
import { UpdateTeamResourceModal } from './CreateTeam/UpdateTeamResourceModal'; import { UpdateTeamResourceModal } from './CreateTeam/UpdateTeamResourceModal';
import { AddTeamRoadmap } from './AddTeamRoadmap';
import { useStore } from '@nanostores/preact'; import { useStore } from '@nanostores/preact';
import { $canManageCurrentTeam } from '../stores/team'; import { $canManageCurrentTeam } from '../stores/team';
import {useToast} from "../hooks/use-toast"; import { useToast } from '../hooks/use-toast';
import { SelectRoadmapModal } from './CreateTeam/SelectRoadmapModal';
export function TeamRoadmaps() { export function TeamRoadmaps() {
const { t: teamId } = getUrlParams(); const { t: teamId } = getUrlParams();
@ -20,6 +21,7 @@ export function TeamRoadmaps() {
const toast = useToast(); const toast = useToast();
const [isLoading, setIsLoading] = useState(true);
const [removingRoadmapId, setRemovingRoadmapId] = useState<string>(''); const [removingRoadmapId, setRemovingRoadmapId] = useState<string>('');
const [isAddingRoadmap, setIsAddingRoadmap] = useState(false); const [isAddingRoadmap, setIsAddingRoadmap] = useState(false);
const [changingRoadmapId, setChangingRoadmapId] = useState<string>(''); const [changingRoadmapId, setChangingRoadmapId] = useState<string>('');
@ -83,12 +85,14 @@ export function TeamRoadmaps() {
return; return;
} }
setIsLoading(true);
Promise.all([ Promise.all([
loadTeam(teamId), loadTeam(teamId),
loadTeamResourceConfig(teamId), loadTeamResourceConfig(teamId),
loadAllRoadmaps(), loadAllRoadmaps(),
]).finally(() => { ]).finally(() => {
pageProgressMessage.set(''); pageProgressMessage.set('');
setIsLoading(false);
}); });
}, [teamId]); }, [teamId]);
@ -97,6 +101,7 @@ export function TeamRoadmaps() {
return; return;
} }
toast.loading('Deleting roadmap');
pageProgressMessage.set(`Deleting roadmap from team`); pageProgressMessage.set(`Deleting roadmap from team`);
const { error, response } = await httpPut<TeamResourceConfig>( const { error, response } = await httpPut<TeamResourceConfig>(
`${import.meta.env.PUBLIC_API_URL}/v1-delete-team-resource-config/${ `${import.meta.env.PUBLIC_API_URL}/v1-delete-team-resource-config/${
@ -117,6 +122,35 @@ export function TeamRoadmaps() {
setResourceConfigs(response); setResourceConfigs(response);
} }
async function onAdd(roadmapId: string) {
if (!teamId) {
return;
}
toast.loading('Adding roadmap');
pageProgressMessage.set('Adding roadmap');
setIsLoading(true);
const { error, response } = await httpPut<TeamResourceConfig>(
`${
import.meta.env.PUBLIC_API_URL
}/v1-update-team-resource-config/${teamId}`,
{
teamId: teamId,
resourceId: roadmapId,
resourceType: 'roadmap',
removed: [],
}
);
if (error || !response) {
toast.error(error?.message || 'Error adding roadmap');
return;
}
setResourceConfigs(response);
toast.success('Roadmap added');
}
async function onRemove(resourceId: string) { async function onRemove(resourceId: string) {
pageProgressMessage.set('Removing roadmap'); pageProgressMessage.set('Removing roadmap');
@ -129,26 +163,56 @@ export function TeamRoadmaps() {
return null; return null;
} }
return ( const addRoadmapModal = isAddingRoadmap && (
<div> <SelectRoadmapModal
{isAddingRoadmap && ( onClose={() => setIsAddingRoadmap(false)}
<AddTeamRoadmap teamResourceConfig={resourceConfigs}
onMakeChanges={(roadmapId) => {
setChangingRoadmapId(roadmapId);
setIsAddingRoadmap(false);
}}
teamId={team?._id!}
setResourceConfigs={setResourceConfigs}
allRoadmaps={allRoadmaps} allRoadmaps={allRoadmaps}
availableRoadmaps={allRoadmaps.filter((r) => { teamId={teamId}
const isAlreadyAdded = resourceConfigs.find( onRoadmapAdd={(roadmapId) => {
(c) => c.resourceId === r.id onAdd(roadmapId).finally(() => {
pageProgressMessage.set('');
});
}}
onRoadmapRemove={(roadmapId) => {
if (confirm('Are you sure you want to remove this roadmap?')) {
onRemove(roadmapId).finally(() => {});
}
}}
/>
); );
return !isAlreadyAdded;
})} if (resourceConfigs.length === 0 && !isLoading) {
onClose={() => setIsAddingRoadmap(false)} return (
<div className="flex flex-col items-center p-4 py-20">
{addRoadmapModal}
<img
alt="roadmap"
src={RoadmapIcon}
className="mb-4 h-24 w-24 opacity-10"
/> />
<h3 className="mb-1 text-2xl font-bold text-gray-900">No roadmaps</h3>
<p className="text-base text-gray-500">
{canManageCurrentTeam
? 'Add a roadmap to start tracking your team'
: 'Ask your team admin to add some roadmaps'}
</p>
{canManageCurrentTeam && (
<button
className="mt-4 rounded-lg bg-black px-4 py-2 font-medium text-white hover:bg-gray-900"
onClick={() => setIsAddingRoadmap(true)}
>
Add roadmap
</button>
)} )}
</div>
);
}
return (
<div>
{addRoadmapModal}
<div className={'grid grid-cols-1 gap-3 sm:grid-cols-2'}> <div className={'grid grid-cols-1 gap-3 sm:grid-cols-2'}>
{changingRoadmapId && ( {changingRoadmapId && (
<UpdateTeamResourceModal <UpdateTeamResourceModal
@ -199,7 +263,7 @@ export function TeamRoadmaps() {
</div> </div>
{canManageCurrentTeam && ( {canManageCurrentTeam && (
<div className={'flex w-full justify-between pt-2 pb-3 px-3'}> <div className={'flex w-full justify-between px-3 pb-3 pt-2'}>
<button <button
type="button" type="button"
className={ className={
@ -219,13 +283,7 @@ export function TeamRoadmaps() {
className={ className={
'text-xs text-red-500 underline hover:text-black focus:outline-none disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:text-red-500' 'text-xs text-red-500 underline hover:text-black focus:outline-none disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:text-red-500'
} }
disabled={resourceConfigs.length === 1}
onClick={() => setRemovingRoadmapId(resourceId)} onClick={() => setRemovingRoadmapId(resourceId)}
title={
resourceConfigs.length === 1
? 'You must have at least one roadmap.'
: 'Delete roadmap from team'
}
> >
Remove Remove
</button> </button>

@ -37,7 +37,7 @@ export function Toaster(props: Props) {
onClick={() => { onClick={() => {
$toastMessage.set(undefined); $toastMessage.set(undefined);
}} }}
className={`fixed bottom-5 left-1/2 max-w-[300px] animate-fade-slide-up min-w-[300px] sm:min-w-[auto] z-50`} className={`fixed bottom-5 left-1/2 z-50 min-w-[300px] max-w-[300px] animate-fade-slide-up sm:min-w-[auto]`}
> >
<div <div
className={`flex -translate-x-1/2 transform cursor-pointer items-center gap-2 rounded-md border border-gray-200 bg-white py-3 pl-4 pr-5 text-black shadow-md hover:bg-gray-50`} className={`flex -translate-x-1/2 transform cursor-pointer items-center gap-2 rounded-md border border-gray-200 bg-white py-3 pl-4 pr-5 text-black shadow-md hover:bg-gray-50`}

Loading…
Cancel
Save