Skip to content

Commit feba6e5

Browse files
Merge pull request #130 from kamranahmedse/master
Create a new pull request by comparing changes across two branches
2 parents b86d6a7 + 5a56b0f commit feba6e5

File tree

32 files changed

+1138
-194
lines changed

32 files changed

+1138
-194
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"image-size": "^1.1.1",
4242
"jose": "^5.2.2",
4343
"js-cookie": "^3.0.5",
44-
"lucide-react": "^0.334.0",
44+
"lucide-react": "^0.358.0",
4545
"nanoid": "^5.0.5",
4646
"nanostores": "^0.9.5",
4747
"node-html-parser": "^6.1.12",

pnpm-lock.yaml

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

readme.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ Roadmaps are now interactive, you can click the nodes to read more about the top
3030

3131
Here is the list of available roadmaps with more being actively worked upon.
3232

33+
> Have a look at the [get started](https://roadmap.sh/get-started) page that might help you pick up a path.
34+
3335
- [Frontend Roadmap](https://roadmap.sh/frontend) / [Frontend Beginner Roadmap](https://roadmap.sh/frontend?r=frontend-beginner)
3436
- [Backend Roadmap](https://roadmap.sh/backend) / [Backend Beginner Roadmap](https://roadmap.sh/backend?r=backend-beginner)
3537
- [DevOps Roadmap](https://roadmap.sh/devops) / [DevOps Beginner Roadmap](https://roadmap.sh/devops?r=devops-beginner)
@@ -83,6 +85,7 @@ There are also interactive best practices:
8385
..and questions to help you test, rate and improve your knowledge
8486

8587
- [JavaScript Questions](https://roadmap.sh/questions/javascript)
88+
- [Node.js Questions](https://roadmap.sh/questions/nodejs)
8689
- [React Questions](https://roadmap.sh/questions/react)
8790

8891
![](https://i.imgur.com/waxVImv.png)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { useEffect, useState } from 'react';
2+
import { httpPatch } from '../../lib/http';
3+
import { setAuthToken } from '../../lib/jwt';
4+
import { Spinner } from '../ReactIcons/Spinner';
5+
import { ErrorIcon2 } from '../ReactIcons/ErrorIcon2';
6+
import { getUrlParams } from '../../lib/browser';
7+
import { CheckIcon } from '../ReactIcons/CheckIcon';
8+
9+
export function TriggerVerifyEmail() {
10+
const { code } = getUrlParams() as { code: string };
11+
12+
// const [isLoading, setIsLoading] = useState(true);
13+
const [status, setStatus] = useState<'loading' | 'error' | 'success'>(
14+
'loading',
15+
);
16+
const [error, setError] = useState('');
17+
18+
const triggerVerify = (code: string) => {
19+
setStatus('loading');
20+
21+
httpPatch<{ token: string }>(
22+
`${import.meta.env.PUBLIC_API_URL}/v1-verify-new-email/${code}`,
23+
{},
24+
)
25+
.then(({ response, error }) => {
26+
if (!response?.token) {
27+
setError(error?.message || 'Something went wrong. Please try again.');
28+
setStatus('error');
29+
30+
return;
31+
}
32+
33+
setAuthToken(response.token);
34+
setStatus('success');
35+
})
36+
.catch((err) => {
37+
setStatus('error');
38+
setError('Something went wrong. Please try again.');
39+
});
40+
};
41+
42+
useEffect(() => {
43+
if (!code) {
44+
setStatus('error');
45+
setError('Something went wrong. Please try again later.');
46+
return;
47+
}
48+
49+
triggerVerify(code);
50+
}, [code]);
51+
52+
const isLoading = status === 'loading';
53+
if (status === 'success') {
54+
return (
55+
<div className="mx-auto flex max-w-md flex-col items-center pt-0 sm:pt-12">
56+
<CheckIcon additionalClasses={'h-16 w-16 opacity-100'} />
57+
<h2 className="mb-1 mt-4 text-center text-xl font-semibold sm:mb-3 sm:mt-4 sm:text-2xl">
58+
Email Update Successful
59+
</h2>
60+
<p className="text-sm sm:text-base">
61+
Your email has been changed successfully. Happy learning!
62+
</p>
63+
</div>
64+
);
65+
}
66+
67+
return (
68+
<div className="mx-auto flex max-w-md flex-col items-center pt-0 sm:pt-12">
69+
<div className="mx-auto max-w-md text-center">
70+
{isLoading && <Spinner className="mx-auto h-16 w-16" />}
71+
{error && <ErrorIcon2 className="mx-auto h-16 w-16" />}
72+
<h2 className="mb-1 mt-4 text-center text-xl font-semibold sm:mb-3 sm:mt-4 sm:text-2xl">
73+
Verifying your new Email
74+
</h2>
75+
<div className="text-sm sm:text-base">
76+
{isLoading && <p>Please wait while we verify your new Email..</p>}
77+
{error && <p className="text-red-700">{error}</p>}
78+
</div>
79+
</div>
80+
</div>
81+
);
82+
}

src/components/CustomRoadmap/CustomRoadmap.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
import { useEffect, useState } from 'react';
22
import { getUrlParams } from '../../lib/browser';
3-
import {
4-
type AppError,
5-
type FetchError,
6-
httpGet,
7-
httpPost,
8-
} from '../../lib/http';
3+
import { type AppError, type FetchError, httpGet } from '../../lib/http';
94
import { RoadmapHeader } from './RoadmapHeader';
105
import { TopicDetail } from '../TopicDetail/TopicDetail';
116
import type { RoadmapDocument } from './CreateRoadmap/CreateRoadmapModal';
127
import { currentRoadmap } from '../../stores/roadmap';
138
import { RestrictedPage } from './RestrictedPage';
14-
import { isLoggedIn } from '../../lib/jwt';
159
import { FlowRoadmapRenderer } from './FlowRoadmapRenderer';
1610

1711
export const allowedLinkTypes = [

src/components/CustomRoadmap/FlowRoadmapRenderer.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,32 @@ export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
125125
}
126126
}, []);
127127

128+
const handleChecklistCheckboxClick = useCallback(
129+
(e: MouseEvent, checklistId: string) => {
130+
const target = e?.currentTarget as HTMLDivElement;
131+
if (!target) {
132+
return;
133+
}
134+
135+
const isCurrentStatusDone = target?.classList.contains('done');
136+
updateTopicStatus(checklistId, isCurrentStatusDone ? 'pending' : 'done');
137+
},
138+
[],
139+
);
140+
141+
const handleChecklistLabelClick = useCallback(
142+
(e: MouseEvent, checklistId: string) => {
143+
const target = e?.currentTarget as HTMLDivElement;
144+
if (!target) {
145+
return;
146+
}
147+
148+
const isCurrentStatusDone = target?.classList.contains('done');
149+
updateTopicStatus(checklistId, isCurrentStatusDone ? 'pending' : 'done');
150+
},
151+
[],
152+
);
153+
128154
return (
129155
<>
130156
{hideRenderer && (
@@ -162,6 +188,8 @@ export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
162188
onTopicAltClick={handleTopicAltClick}
163189
onButtonNodeClick={handleLinkClick}
164190
onLinkClick={handleLinkClick}
191+
onChecklistCheckboxClick={handleChecklistCheckboxClick}
192+
onChecklistLableClick={handleChecklistLabelClick}
165193
fontFamily="Balsamiq Sans"
166194
fontURL="/fonts/balsamiq.woff2"
167195
/>

src/components/Footer.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import Icon from './AstroIcon.astro';
5151
href='https://kamranahmed.info'
5252
target='_blank'
5353
>
54-
<span class='hidden sm:inline'>@kamrify</span>
54+
<span class='hidden sm:inline'>Kamran</span>
5555
<span class='inline sm:hidden'>Kamran Ahmed</span>
5656
</a>
5757
</p>

src/components/FrameRenderer/renderer.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {
1414
import { pageProgressMessage } from '../../stores/page';
1515
import { showLoginPopup } from '../../lib/popup';
1616
import { replaceChildren } from '../../lib/dom.ts';
17+
import {setUrlParams} from "../../lib/browser.ts";
1718

1819
export class Renderer {
1920
resourceId: string;
@@ -141,19 +142,8 @@ export class Renderer {
141142

142143
const newJsonFileSlug = newJsonUrl.split('/').pop()?.replace('.json', '');
143144

144-
// Update the URL and attach the new roadmap type
145-
if (window?.history?.pushState) {
146-
const url = new URL(window.location.href);
147-
const type = this.resourceType[0]; // r for roadmap, b for best-practices
148-
149-
url.searchParams.delete(type);
150-
151-
if (newJsonFileSlug !== this.resourceId) {
152-
url.searchParams.set(type, newJsonFileSlug!);
153-
}
154-
155-
window.history.pushState(null, '', url.toString());
156-
}
145+
const type = this.resourceType[0]; // r for roadmap, b for best-practices
146+
setUrlParams({ [type]: newJsonFileSlug! })
157147

158148
this.jsonToSvg(newJsonUrl)?.then(() => {});
159149
}

src/components/GenerateRoadmap/RoadmapTopicDetail.tsx

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
33
import { useKeydown } from '../../hooks/use-keydown';
44
import { useOutsideClick } from '../../hooks/use-outside-click';
55
import { markdownToHtml } from '../../lib/markdown';
6-
import { Ban, Cog, FileText, X } from 'lucide-react';
6+
import {Ban, Cog, Contact, FileText, User, UserRound, X} from 'lucide-react';
77
import { Spinner } from '../ReactIcons/Spinner';
88
import type { RoadmapNodeDetails } from './GenerateRoadmap';
99
import { getOpenAIKey, isLoggedIn, removeAuthToken } from '../../lib/jwt';
@@ -43,12 +43,10 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
4343
const generateAiRoadmapTopicContent = async () => {
4444
setIsLoading(true);
4545
setError('');
46-
//
47-
// if (topicLimitUsed >= topicLimit) {
48-
// setError('Maximum limit reached');
49-
// setIsLoading(false);
50-
// return;
51-
// }
46+
47+
if (!isLoggedIn()) {
48+
return;
49+
}
5250

5351
if (!roadmapId || !nodeTitle) {
5452
setIsLoading(false);
@@ -133,50 +131,44 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
133131
tabIndex={0}
134132
className="fixed right-0 top-0 z-40 h-screen w-full overflow-y-auto bg-white p-4 focus:outline-0 sm:max-w-[600px] sm:p-6"
135133
>
136-
<div className="flex flex-col items-start gap-2 sm:flex-row">
137-
<span>
138-
<span
139-
className={cn(
140-
'mr-0.5 inline-block rounded-xl border px-1.5 text-center text-sm tabular-nums text-gray-800',
141-
{
142-
'animate-pulse border-zinc-300 bg-zinc-300 text-zinc-300':
143-
!topicLimit,
144-
},
145-
)}
146-
>
147-
{topicLimitUsed} of {topicLimit}
148-
</span>{' '}
149-
topics generated
150-
</span>
151-
{!isLoggedIn() && (
152-
<button
153-
className="rounded-xl border border-current px-1.5 py-0.5 text-left text-sm font-medium text-blue-500 sm:text-center"
154-
onClick={showLoginPopup}
155-
>
156-
Generate more by <span className="font-semibold">logging in</span>
157-
</button>
158-
)}
159-
{isLoggedIn() && !openAIKey && (
160-
<button
161-
className="rounded-xl border border-current px-1.5 py-0.5 text-left text-sm font-medium text-blue-500 sm:text-center"
162-
onClick={onConfigureOpenAI}
163-
>
164-
Need to generate more?{' '}
165-
<span className="font-semibold">Click here.</span>
166-
</button>
167-
)}
168-
{isLoggedIn() && openAIKey && (
169-
<button
170-
className="flex items-center gap-1 rounded-xl border border-current px-1.5 py-0.5 text-left text-sm font-medium text-blue-500 sm:text-center"
171-
onClick={onConfigureOpenAI}
172-
>
173-
<Cog className="-mt-0.5 inline-block h-4 w-4" />
174-
Configure OpenAI Key
175-
</button>
176-
)}
177-
</div>
134+
{isLoggedIn() && (
135+
<div className="flex flex-col items-start gap-2 sm:flex-row">
136+
<span>
137+
<span
138+
className={cn(
139+
'mr-0.5 inline-block rounded-xl border px-1.5 text-center text-sm tabular-nums text-gray-800',
140+
{
141+
'animate-pulse border-zinc-300 bg-zinc-300 text-zinc-300':
142+
!topicLimit,
143+
},
144+
)}
145+
>
146+
{topicLimitUsed} of {topicLimit}
147+
</span>{' '}
148+
topics generated
149+
</span>
150+
{!openAIKey && (
151+
<button
152+
className="rounded-xl border border-current px-1.5 py-0.5 text-left text-sm font-medium text-blue-500 sm:text-center"
153+
onClick={onConfigureOpenAI}
154+
>
155+
Need to generate more?{' '}
156+
<span className="font-semibold">Click here.</span>
157+
</button>
158+
)}
159+
{openAIKey && (
160+
<button
161+
className="flex items-center gap-1 rounded-xl border border-current px-1.5 py-0.5 text-left text-sm font-medium text-blue-500 sm:text-center"
162+
onClick={onConfigureOpenAI}
163+
>
164+
<Cog className="-mt-0.5 inline-block h-4 w-4" />
165+
Configure OpenAI Key
166+
</button>
167+
)}
168+
</div>
169+
)}
178170

179-
{isLoading && (
171+
{isLoggedIn() && isLoading && (
180172
<div className="mt-6 flex w-full justify-center">
181173
<Spinner
182174
outerFill="#d1d5db"
@@ -186,6 +178,22 @@ export function RoadmapTopicDetail(props: RoadmapTopicDetailProps) {
186178
</div>
187179
)}
188180

181+
{!isLoggedIn() && (
182+
<div className="flex h-full flex-col items-center justify-center">
183+
<Contact className="h-14 w-14 text-gray-200 mb-3.5" />
184+
<h2 className='font-medium text-xl'>You must be logged in</h2>
185+
<p className="text-base text-gray-400">
186+
Sign up or login to generate topic content.
187+
</p>
188+
<button
189+
className="mt-3.5 text-base font-medium text-white bg-black px-3 py-2 rounded-md w-full max-w-[300px]"
190+
onClick={showLoginPopup}
191+
>
192+
Sign up / Login
193+
</button>
194+
</div>
195+
)}
196+
189197
{!isLoading && !error && (
190198
<>
191199
<div className="mb-2">

src/components/NavigationDropdown.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ export function NavigationDropdown() {
7373
</button>
7474
<div
7575
className={cn(
76-
'absolute pointer-events-none left-0 top-full z-[999] mt-2 w-48 min-w-[320px] -translate-y-1 rounded-lg bg-slate-800 py-2 opacity-0 shadow-xl transition-all duration-100',
76+
'absolute pointer-events-none invisible left-0 top-full z-[999] mt-2 w-48 min-w-[320px] -translate-y-1 rounded-lg bg-slate-800 py-2 opacity-0 shadow-xl transition-all duration-100',
7777
{
78-
'pointer-events-auto translate-y-2.5 opacity-100': isOpen,
78+
'pointer-events-auto visible translate-y-2.5 opacity-100': isOpen,
7979
},
8080
)}
8181
>

0 commit comments

Comments
 (0)