From 20be28653fbedb02ec81853d351d4a800f1eafc0 Mon Sep 17 00:00:00 2001
From: guangwu
Date: Sat, 4 May 2024 11:10:54 +0800
Subject: [PATCH 1/7] fix: following typo (#5308)
* fix: typo
* fix: gap
---------
Co-authored-by: Arik Chakma
---
src/data/roadmaps/typescript/content/100-typescript/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/data/roadmaps/typescript/content/100-typescript/index.md b/src/data/roadmaps/typescript/content/100-typescript/index.md
index 6074c52d2..80c9dc4eb 100644
--- a/src/data/roadmaps/typescript/content/100-typescript/index.md
+++ b/src/data/roadmaps/typescript/content/100-typescript/index.md
@@ -9,7 +9,7 @@ The main benefits of using TypeScript include:
- Improved Maintainability
- Backwards Compatibility
-Learn more from the folowing links:
+Learn more from the following links:
- [Overview of TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html)
- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html)
From 2b20996134362e2f24e6dccbc2bc09440c8eb2db Mon Sep 17 00:00:00 2001
From: Kamran Ahmed
Date: Sun, 5 May 2024 18:50:27 +0100
Subject: [PATCH 2/7] Show proper error on open ai model
---
.../GenerateRoadmap/OpenAISettings.tsx | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/src/components/GenerateRoadmap/OpenAISettings.tsx b/src/components/GenerateRoadmap/OpenAISettings.tsx
index 5cb6f897c..181a8347b 100644
--- a/src/components/GenerateRoadmap/OpenAISettings.tsx
+++ b/src/components/GenerateRoadmap/OpenAISettings.tsx
@@ -1,4 +1,3 @@
-import { Modal } from '../Modal.tsx';
import { useEffect, useState } from 'react';
import { deleteOpenAIKey, getOpenAIKey, saveOpenAIKey } from '../../lib/jwt.ts';
import { cn } from '../../lib/classname.ts';
@@ -17,7 +16,7 @@ export function OpenAISettings(props: OpenAISettingsProps) {
const [defaultOpenAIKey, setDefaultOpenAIKey] = useState('');
- const [hasError, setHasError] = useState(false);
+ const [error, setError] = useState('');
const [openaiApiKey, setOpenaiApiKey] = useState('');
const [isLoading, setIsLoading] = useState(false);
@@ -57,7 +56,7 @@ export function OpenAISettings(props: OpenAISettingsProps) {
className="mt-4"
onSubmit={async (e) => {
e.preventDefault();
- setHasError(false);
+ setError('');
const normalizedKey = openaiApiKey.trim();
if (!normalizedKey) {
@@ -68,7 +67,7 @@ export function OpenAISettings(props: OpenAISettingsProps) {
}
if (!normalizedKey.startsWith('sk-')) {
- setHasError(true);
+ setError("Invalid OpenAI API key. It should start with 'sk-'");
return;
}
@@ -81,7 +80,7 @@ export function OpenAISettings(props: OpenAISettingsProps) {
);
if (error) {
- setHasError(true);
+ setError(error.message);
setIsLoading(false);
return;
}
@@ -100,13 +99,13 @@ export function OpenAISettings(props: OpenAISettingsProps) {
className={cn(
'block w-full rounded-md border border-gray-300 px-3 py-2 text-gray-800 transition-colors focus:border-black focus:outline-none',
{
- 'border-red-500 bg-red-100 focus:border-red-500': hasError,
+ 'border-red-500 bg-red-100 focus:border-red-500': error,
},
)}
placeholder="Enter your OpenAI API key"
value={openaiApiKey}
onChange={(e) => {
- setHasError(false);
+ setError('');
setOpenaiApiKey((e.target as HTMLInputElement).value);
}}
/>
@@ -127,9 +126,9 @@ export function OpenAISettings(props: OpenAISettingsProps) {
We do not store your API key on our servers.
- {hasError && (
+ {error && (
- Please enter a valid OpenAI API key
+ {error}
)}
diff --git a/src/env.d.ts b/src/env.d.ts
index d52cd1cc6..7218154c2 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -4,6 +4,7 @@
interface ImportMetaEnv {
GITHUB_SHA: string;
PUBLIC_API_URL: string;
+ PUBLIC_APP_URL: string;
PUBLIC_AVATAR_BASE_URL: string;
PUBLIC_EDITOR_APP_URL: string;
}
diff --git a/src/lib/road-card.ts b/src/lib/road-card.ts
new file mode 100644
index 000000000..8aec82c1d
--- /dev/null
+++ b/src/lib/road-card.ts
@@ -0,0 +1,17 @@
+export async function getRoadCard(
+ version: 'tall' | 'wide',
+ userId: string,
+ variant: 'dark' | 'light',
+ roadmaps: string = '',
+) {
+ const url = new URL(
+ `${import.meta.env.PUBLIC_API_URL}/v1-badge/${version}/${userId}`,
+ );
+ url.searchParams.set('variant', variant);
+ if (roadmaps) {
+ url.searchParams.set('roadmaps', roadmaps);
+ }
+
+ const response = await fetch(url.toString());
+ return response.text();
+}
diff --git a/src/pages/card/[version]/[userId].ts b/src/pages/card/[version]/[userId].ts
new file mode 100644
index 000000000..a3a974b60
--- /dev/null
+++ b/src/pages/card/[version]/[userId].ts
@@ -0,0 +1,34 @@
+import type { APIRoute } from 'astro';
+import { getDefaultOpenGraphImageBuffer } from '../../../lib/open-graph';
+import { getRoadCard } from '../../../lib/road-card';
+
+export const prerender = false;
+
+type Params = {
+ version: 'tall' | 'wide';
+ userId: string;
+};
+
+export const GET: APIRoute = async (context) => {
+ const { userId, version } = context.params;
+
+ if (!userId || !version) {
+ const buffer = await getDefaultOpenGraphImageBuffer();
+ return new Response(buffer, {
+ headers: {
+ 'Content-Type': 'image/png',
+ },
+ });
+ }
+
+ const searchParams = new URLSearchParams(context.url.searchParams);
+ const variant = (searchParams.get('variant') as 'dark' | 'light') || 'dark';
+ const roadmaps = searchParams.get('roadmaps') || '';
+
+ const svg = await getRoadCard(version, userId, variant, roadmaps);
+ return new Response(svg, {
+ headers: {
+ 'Content-Type': 'image/svg+xml',
+ },
+ });
+};
From 66e4793032a79b4666b0e8f3c308f6bfaafa999f Mon Sep 17 00:00:00 2001
From: Arik Chakma
Date: Tue, 7 May 2024 19:56:44 +0600
Subject: [PATCH 6/7] feat: team personal progress only (#5586)
* feat: team personal progress only
* fix: default false
---
src/components/CreateTeam/CreateTeamForm.tsx | 23 +++--
src/components/CreateTeam/Step1.tsx | 24 ++++--
.../TeamActivity/TeamActivityPage.tsx | 84 +++++++++++++------
.../TeamSettings/UpdateTeamForm.tsx | 40 +++++++--
tsconfig.json | 1 +
5 files changed, 121 insertions(+), 51 deletions(-)
diff --git a/src/components/CreateTeam/CreateTeamForm.tsx b/src/components/CreateTeam/CreateTeamForm.tsx
index b06df0c96..84fd33fae 100644
--- a/src/components/CreateTeam/CreateTeamForm.tsx
+++ b/src/components/CreateTeam/CreateTeamForm.tsx
@@ -9,7 +9,7 @@ import { pageProgressMessage } from '../../stores/page';
import type { TeamResourceConfig } from './RoadmapSelector';
import { Step3 } from './Step3';
import { Step4 } from './Step4';
-import {useToast} from "../../hooks/use-toast";
+import { useToast } from '../../hooks/use-toast';
export interface TeamDocument {
_id?: string;
@@ -22,6 +22,7 @@ export interface TeamDocument {
linkedIn?: string;
};
type: ValidTeamType;
+ personalProgressOnly?: boolean;
canMemberSendInvite: boolean;
teamSize?: ValidTeamSize;
createdAt: Date;
@@ -40,10 +41,10 @@ export function CreateTeamForm() {
async function loadTeam(
teamIdToFetch: string,
- requiredStepIndex: number | string
+ requiredStepIndex: number | string,
) {
const { response, error } = await httpGet(
- `${import.meta.env.PUBLIC_API_URL}/v1-get-team/${teamIdToFetch}`
+ `${import.meta.env.PUBLIC_API_URL}/v1-get-team/${teamIdToFetch}`,
);
if (error || !response) {
@@ -70,7 +71,7 @@ export function CreateTeamForm() {
async function loadTeamResourceConfig(teamId: string) {
const { error, response } = await httpGet(
- `${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)) {
console.error(error);
@@ -96,7 +97,7 @@ export function CreateTeamForm() {
}, [teamId, queryStepIndex]);
const [selectedTeamType, setSelectedTeamType] = useState(
- team?.type || 'company'
+ team?.type || 'company',
);
const [completedSteps, setCompletedSteps] = useState([0]);
@@ -191,13 +192,17 @@ export function CreateTeamForm() {
return (
-
-
Create Team
-
+
+
Create Team
+
Complete the steps below to create your team
-
+
(
- team?.teamSize || ('' as any)
+ team?.teamSize || ('' as any),
);
const handleSubmit = async (e: FormEvent) => {
@@ -74,7 +74,7 @@ export function Step1(props: Step1Props) {
}),
roadmapIds: [],
bestPracticeIds: [],
- }
+ },
));
if (error || !response?._id) {
@@ -96,7 +96,7 @@ export function Step1(props: Step1Props) {
teamSize,
linkedInUrl: linkedInUrl || undefined,
}),
- }
+ },
));
if (error || (response as any)?.status !== 'ok') {
@@ -168,7 +168,10 @@ export function Step1(props: Step1Props) {
{selectedTeamType === 'company' && (
-
diff --git a/src/components/TeamActivity/TeamActivityPage.tsx b/src/components/TeamActivity/TeamActivityPage.tsx
index 1c124214e..2b4a4a4ce 100644
--- a/src/components/TeamActivity/TeamActivityPage.tsx
+++ b/src/components/TeamActivity/TeamActivityPage.tsx
@@ -98,38 +98,70 @@ export function TeamActivityPage() {
}, [teamId]);
const { users, activities } = teamActivities?.data;
- const usersWithActivities = useMemo(() => {
- const validActivities = activities.filter((activity) => {
+ const validActivities = useMemo(() => {
+ return activities?.filter((activity) => {
return (
activity.activity.length > 0 &&
activity.activity.some((t) => (t?.topicIds?.length || 0) > 0)
);
});
+ }, [activities]);
+
+ const sortedUniqueCreatedAt = useMemo(() => {
+ return new Set(
+ validActivities
+ ?.map((activity) => new Date(activity.createdAt).setHours(0, 0, 0, 0))
+ .sort((a, b) => {
+ return new Date(b).getTime() - new Date(a).getTime();
+ }),
+ );
+ }, [validActivities]);
+
+ const usersWithActivities = useMemo(() => {
+ const enrichedUsers: {
+ _id: string;
+ name: string;
+ avatar?: string;
+ username?: string;
+ activities: TeamStreamActivity[];
+ }[] = [];
+
+ for (const uniqueCreatedAt of sortedUniqueCreatedAt) {
+ const uniqueActivities = validActivities.filter(
+ (activity) =>
+ new Date(activity.createdAt).setHours(0, 0, 0, 0) === uniqueCreatedAt,
+ );
+
+ const usersWithUniqueActivities = users
+ .map((user) => {
+ const userActivities = uniqueActivities
+ .filter((activity) => activity.userId === user._id)
+ .flatMap((activity) => activity.activity)
+ .filter((activity) => (activity?.topicIds?.length || 0) > 0)
+ .sort((a, b) => {
+ return (
+ new Date(b.updatedAt).getTime() -
+ new Date(a.updatedAt).getTime()
+ );
+ });
+
+ return {
+ ...user,
+ activities: userActivities,
+ };
+ })
+ .filter((user) => user.activities.length > 0)
+ .sort((a, b) => {
+ return (
+ new Date(b.activities[0].updatedAt).getTime() -
+ new Date(a.activities[0].updatedAt).getTime()
+ );
+ });
+
+ enrichedUsers.push(...usersWithUniqueActivities);
+ }
- return users
- .map((user) => {
- const userActivities = validActivities
- .filter((activity) => activity.userId === user._id)
- .flatMap((activity) => activity.activity)
- .filter((activity) => (activity?.topicIds?.length || 0) > 0)
- .sort((a, b) => {
- return (
- new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
- );
- });
-
- return {
- ...user,
- activities: userActivities,
- };
- })
- .filter((user) => user.activities.length > 0)
- .sort((a, b) => {
- return (
- new Date(b.activities[0].updatedAt).getTime() -
- new Date(a.activities[0].updatedAt).getTime()
- );
- });
+ return enrichedUsers;
}, [users, activities]);
if (!teamId) {
diff --git a/src/components/TeamSettings/UpdateTeamForm.tsx b/src/components/TeamSettings/UpdateTeamForm.tsx
index 4e78137f4..b8ef2ae52 100644
--- a/src/components/TeamSettings/UpdateTeamForm.tsx
+++ b/src/components/TeamSettings/UpdateTeamForm.tsx
@@ -24,6 +24,7 @@ export function UpdateTeamForm() {
const [gitHub, setGitHub] = useState('');
const [teamType, setTeamType] = useState('');
const [teamSize, setTeamSize] = useState('');
+ const [personalProgressOnly, setPersonalProgressOnly] = useState(false);
const validTeamSizes = [
'0-1',
'2-10',
@@ -55,11 +56,12 @@ export function UpdateTeamForm() {
website,
type: teamType,
gitHubUrl: gitHub || undefined,
+ personalProgressOnly,
...(teamType === 'company' && {
teamSize,
linkedInUrl: linkedIn || undefined,
}),
- }
+ },
);
if (error) {
@@ -77,7 +79,7 @@ export function UpdateTeamForm() {
async function loadTeam() {
const { response, error } = await httpGet(
- `${import.meta.env.PUBLIC_API_URL}/v1-get-team/${teamId}`
+ `${import.meta.env.PUBLIC_API_URL}/v1-get-team/${teamId}`,
);
if (error || !response) {
console.log(error);
@@ -90,6 +92,7 @@ export function UpdateTeamForm() {
setLinkedIn(response?.links?.linkedIn || '');
setGitHub(response?.links?.github || '');
setTeamType(response.type);
+ setPersonalProgressOnly(response.personalProgressOnly ?? false);
if (response.teamSize) {
setTeamSize(response.teamSize);
}
@@ -205,16 +208,14 @@ export function UpdateTeamForm() {
@@ -231,7 +232,7 @@ export function UpdateTeamForm() {