mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-06 18:31:00 +00:00
Can now link anon account with github, improvements to dev journey, maybe broke some auth stufff
This commit is contained in:
parent
003eecb551
commit
004d388c82
@ -65,9 +65,6 @@ const ActivityContributionChart = ({ session }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('All Learning Activities:', allActivities);
|
|
||||||
console.log('Activities by Date:', activityData);
|
|
||||||
|
|
||||||
setContributionData(activityData);
|
setContributionData(activityData);
|
||||||
setTotalActivities(Object.values(activityData).reduce((a, b) => a + b, 0));
|
setTotalActivities(Object.values(activityData).reduce((a, b) => a + b, 0));
|
||||||
|
|
||||||
@ -112,15 +109,6 @@ const ActivityContributionChart = ({ session }) => {
|
|||||||
const dateString = currentDate.toLocaleDateString('en-CA'); // YYYY-MM-DD format
|
const dateString = currentDate.toLocaleDateString('en-CA'); // YYYY-MM-DD format
|
||||||
const activityCount = contributionData[dateString] || 0;
|
const activityCount = contributionData[dateString] || 0;
|
||||||
|
|
||||||
// Debug log
|
|
||||||
if (activityCount > 0) {
|
|
||||||
console.log('Found activity:', {
|
|
||||||
date: currentDate.toDateString(),
|
|
||||||
dateString,
|
|
||||||
activityCount
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
calendar[weekDay].push({
|
calendar[weekDay].push({
|
||||||
date: new Date(currentDate),
|
date: new Date(currentDate),
|
||||||
count: activityCount
|
count: activityCount
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { ProgressBar } from 'primereact/progressbar';
|
import { ProgressBar } from 'primereact/progressbar';
|
||||||
import { Accordion, AccordionTab } from 'primereact/accordion';
|
import { Accordion, AccordionTab } from 'primereact/accordion';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession, signIn } from 'next-auth/react';
|
||||||
|
import GenericButton from '@/components/buttons/GenericButton';
|
||||||
|
|
||||||
const allTasks = [
|
const allTasks = [
|
||||||
{
|
{
|
||||||
@ -18,11 +19,10 @@ const allTasks = [
|
|||||||
status: 'PlebDevs Starter',
|
status: 'PlebDevs Starter',
|
||||||
completed: false,
|
completed: false,
|
||||||
tier: 'New Dev',
|
tier: 'New Dev',
|
||||||
courseId: "f538f5c5-1a72-4804-8eb1-3f05cea64874",
|
// courseId: "f538f5c5-1a72-4804-8eb1-3f05cea64874",
|
||||||
|
courseId: "f6daa88a-53d6-4901-8dbd-d2203a05b7ab",
|
||||||
subTasks: [
|
subTasks: [
|
||||||
{ status: 'Connect GitHub', completed: false },
|
{ status: 'Complete the course', completed: false },
|
||||||
{ status: 'Create First GitHub Repo', completed: false },
|
|
||||||
{ status: 'Push Commit', completed: false }
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -32,7 +32,7 @@ const allTasks = [
|
|||||||
courseId: 'f73c37f4-df2e-4f7d-a838-dce568c76136',
|
courseId: 'f73c37f4-df2e-4f7d-a838-dce568c76136',
|
||||||
subTasks: [
|
subTasks: [
|
||||||
{ status: 'Complete the course', completed: false },
|
{ status: 'Complete the course', completed: false },
|
||||||
{ status: 'Submit Link to completed project', completed: false },
|
{ status: 'Select your completed project', completed: false },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -42,19 +42,19 @@ const allTasks = [
|
|||||||
courseId: 'f6825391-831c-44da-904a-9ac3d149b7be',
|
courseId: 'f6825391-831c-44da-904a-9ac3d149b7be',
|
||||||
subTasks: [
|
subTasks: [
|
||||||
{ status: 'Complete the course', completed: false },
|
{ status: 'Complete the course', completed: false },
|
||||||
{ status: 'Submit Link to completed project', completed: false },
|
{ status: 'Select your completed project', completed: false },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const UserProgress = () => {
|
const UserProgress = () => {
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
const [currentTier, setCurrentTier] = useState('Pleb');
|
const [currentTier, setCurrentTier] = useState(null);
|
||||||
const [expandedItems, setExpandedItems] = useState({});
|
const [expandedItems, setExpandedItems] = useState({});
|
||||||
const [completedCourses, setCompletedCourses] = useState([]);
|
const [completedCourses, setCompletedCourses] = useState([]);
|
||||||
const [tasks, setTasks] = useState([]);
|
const [tasks, setTasks] = useState([]);
|
||||||
|
|
||||||
const { data: session } = useSession();
|
const { data: session, update } = useSession();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session?.user) {
|
if (session?.user) {
|
||||||
@ -74,20 +74,37 @@ const UserProgress = () => {
|
|||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
const generateTasks = (completedCourseIds) => {
|
const generateTasks = (completedCourseIds) => {
|
||||||
const updatedTasks = allTasks.map(task => ({
|
const updatedTasks = allTasks.map(task => {
|
||||||
...task,
|
if (task.status === 'Connect GitHub') {
|
||||||
completed: task.courseId === null || completedCourseIds.includes(task.courseId),
|
return {
|
||||||
subTasks: task.subTasks ? task.subTasks.map(subTask => ({
|
...task,
|
||||||
...subTask,
|
completed: session?.githubProfile ? true : false,
|
||||||
completed: completedCourseIds.includes(task.courseId)
|
subTasks: task.subTasks ? task.subTasks.map(subTask => ({
|
||||||
})) : undefined
|
...subTask,
|
||||||
}));
|
completed: session?.githubProfile ? true : false
|
||||||
|
})) : undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...task,
|
||||||
|
completed: task.courseId === null || completedCourseIds.includes(task.courseId),
|
||||||
|
subTasks: task.subTasks ? task.subTasks.map(subTask => ({
|
||||||
|
...subTask,
|
||||||
|
completed: completedCourseIds.includes(task.courseId)
|
||||||
|
})) : undefined
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
setTasks(updatedTasks);
|
setTasks(updatedTasks);
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateProgress = (completedCourseIds) => {
|
const calculateProgress = (completedCourseIds) => {
|
||||||
let progressValue = 25;
|
let progressValue = 0;
|
||||||
|
|
||||||
|
if (session?.githubProfile) {
|
||||||
|
progressValue += 25;
|
||||||
|
}
|
||||||
|
|
||||||
const remainingTasks = allTasks.slice(1);
|
const remainingTasks = allTasks.slice(1);
|
||||||
remainingTasks.forEach(task => {
|
remainingTasks.forEach(task => {
|
||||||
@ -100,16 +117,16 @@ const UserProgress = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const calculateCurrentTier = (completedCourseIds) => {
|
const calculateCurrentTier = (completedCourseIds) => {
|
||||||
let tier = 'Pleb';
|
let tier = null;
|
||||||
|
|
||||||
if (completedCourseIds.includes("f538f5c5-1a72-4804-8eb1-3f05cea64874")) {
|
|
||||||
tier = 'New Dev';
|
|
||||||
}
|
|
||||||
if (completedCourseIds.includes("f73c37f4-df2e-4f7d-a838-dce568c76136")) {
|
|
||||||
tier = 'Junior Dev';
|
|
||||||
}
|
|
||||||
if (completedCourseIds.includes("f6825391-831c-44da-904a-9ac3d149b7be")) {
|
if (completedCourseIds.includes("f6825391-831c-44da-904a-9ac3d149b7be")) {
|
||||||
tier = 'Plebdev';
|
tier = 'Plebdev';
|
||||||
|
} else if (completedCourseIds.includes("f73c37f4-df2e-4f7d-a838-dce568c76136")) {
|
||||||
|
tier = 'Junior Dev';
|
||||||
|
} else if (completedCourseIds.includes("f6daa88a-53d6-4901-8dbd-d2203a05b7ab")) {
|
||||||
|
tier = 'New Dev';
|
||||||
|
} else if (session?.githubProfile) {
|
||||||
|
tier = 'Pleb';
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentTier(tier);
|
setCurrentTier(tier);
|
||||||
@ -122,6 +139,12 @@ const UserProgress = () => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleGitHubLink = async () => {
|
||||||
|
if (!session?.user?.id) return;
|
||||||
|
|
||||||
|
await signIn("github");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-gray-800 rounded-3xl p-6 w-[500px] max-mob:w-full max-tab:w-full mx-auto my-8">
|
<div className="bg-gray-800 rounded-3xl p-6 w-[500px] max-mob:w-full max-tab:w-full mx-auto my-8">
|
||||||
<h1 className="text-3xl font-bold text-white mb-2">Dev Journey</h1>
|
<h1 className="text-3xl font-bold text-white mb-2">Dev Journey</h1>
|
||||||
@ -139,30 +162,92 @@ const UserProgress = () => {
|
|||||||
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<span className="text-white text-lg font-semibold">Current Tier: </span>
|
<span className="text-white text-lg font-semibold">Current Tier: </span>
|
||||||
<span className="bg-green-500 text-white px-3 py-1 rounded-full">{currentTier}</span>
|
{currentTier ? (
|
||||||
|
<span className="bg-green-500 text-white px-3 py-1 rounded-full">
|
||||||
|
{currentTier}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="bg-gray-700 text-gray-400 px-3 py-1 rounded-full text-sm">
|
||||||
|
Not Started
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul className="space-y-4 mb-6">
|
<ul className="space-y-4 mb-6">
|
||||||
{tasks.map((task, index) => (
|
{tasks.map((task, index) => (
|
||||||
console.log(task),
|
|
||||||
<li key={index}>
|
<li key={index}>
|
||||||
<div className="flex items-center justify-between">
|
<Accordion
|
||||||
<div className="flex items-center">
|
activeIndex={expandedItems[index] ? 0 : null}
|
||||||
{task.completed ? (
|
onTabChange={(e) => handleAccordionChange(index, e.index === 0)}
|
||||||
<div className="w-6 h-6 bg-green-500 rounded-full flex items-center justify-center mr-3">
|
>
|
||||||
<i className="pi pi-check text-white text-lg"></i>
|
<AccordionTab
|
||||||
|
header={
|
||||||
|
<div className="flex items-center justify-between w-full">
|
||||||
|
<div className="flex items-center">
|
||||||
|
{task.completed ? (
|
||||||
|
<div className="w-6 h-6 bg-green-500 rounded-full flex items-center justify-center mr-3">
|
||||||
|
<i className="pi pi-check text-white text-lg"></i>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="w-6 h-6 bg-gray-700 rounded-full flex items-center justify-center mr-3">
|
||||||
|
<i className="pi pi-info-circle text-white text-lg"></i>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<span className={`text-lg ${task.completed ? 'text-white' : 'text-gray-400'}`}>{task.status}</span>
|
||||||
|
</div>
|
||||||
|
<span className="bg-blue-500 text-white text-xs px-2 py-1 rounded-full w-20 text-center">
|
||||||
|
{task.tier}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
}
|
||||||
<div className="w-6 h-6 bg-gray-700 rounded-full flex items-center justify-center mr-3">
|
>
|
||||||
<i className="pi pi-info-circle text-white text-lg"></i>
|
{task.status === 'Connect GitHub' && !task.completed && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<GenericButton
|
||||||
|
label="Connect GitHub"
|
||||||
|
icon="pi pi-github"
|
||||||
|
onClick={handleGitHubLink}
|
||||||
|
className="w-fit bg-[#24292e] hover:bg-[#2f363d] border border-[#f8f8ff] text-[#f8f8ff] font-semibold"
|
||||||
|
rounded
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<span className={`text-lg ${task.completed ? 'text-white' : 'text-gray-400'}`}>{task.status}</span>
|
{task.subTasks && (
|
||||||
</div>
|
<ul className="space-y-2">
|
||||||
<span className="bg-blue-500 text-white text-xs px-2 py-1 rounded-full w-20 text-center">
|
{task.subTasks.map((subTask, subIndex) => (
|
||||||
{task.tier}
|
<li key={subIndex} className="flex items-center">
|
||||||
</span>
|
{subTask.completed ? (
|
||||||
</div>
|
<div className="w-4 h-4 bg-green-500 rounded-full flex items-center justify-center mr-3">
|
||||||
|
<i className="pi pi-check text-white text-sm"></i>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="w-4 h-4 bg-gray-700 rounded-full flex items-center justify-center mr-3">
|
||||||
|
<i className="pi pi-info-circle text-white text-sm"></i>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<span className={`${subTask.completed ? 'text-white' : 'text-gray-400'}`}>
|
||||||
|
{subTask.status}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
{task.courseId && (
|
||||||
|
<div className="mt-2 flex justify-end">
|
||||||
|
<GenericButton
|
||||||
|
icon="pi pi-external-link"
|
||||||
|
onClick={() => router.push(`/courses/${task.courseId}`)}
|
||||||
|
tooltip="View Course"
|
||||||
|
tooltipOptions={{
|
||||||
|
position: "top"
|
||||||
|
}}
|
||||||
|
outlined
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</AccordionTab>
|
||||||
|
</Accordion>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -12,8 +12,6 @@ import { createRole } from "@/db/models/roleModels";
|
|||||||
import appConfig from "@/config/appConfig";
|
import appConfig from "@/config/appConfig";
|
||||||
import GithubProvider from "next-auth/providers/github";
|
import GithubProvider from "next-auth/providers/github";
|
||||||
import NDK from "@nostr-dev-kit/ndk";
|
import NDK from "@nostr-dev-kit/ndk";
|
||||||
import { SimplePool } from "nostr-tools";
|
|
||||||
import { finalizeEvent } from "nostr-tools";
|
|
||||||
|
|
||||||
// todo: currently email accounts ephemeral privkey gets saved to db but not anon user, is this required at all given the newer auth setup?
|
// todo: currently email accounts ephemeral privkey gets saved to db but not anon user, is this required at all given the newer auth setup?
|
||||||
|
|
||||||
@ -40,7 +38,7 @@ const authorize = async (pubkey) => {
|
|||||||
dbUser = await getUserByPubkey(pubkey);
|
dbUser = await getUserByPubkey(pubkey);
|
||||||
}
|
}
|
||||||
} else if (fields.username !== dbUser.username) {
|
} else if (fields.username !== dbUser.username) {
|
||||||
const updatedUser = await updateUser(dbUser.id, { username: fields.username });
|
const updatedUser = await updateUser(dbUser.id, { username: fields.username, name: fields.username });
|
||||||
if (updatedUser) {
|
if (updatedUser) {
|
||||||
dbUser = await getUserByPubkey(pubkey);
|
dbUser = await getUserByPubkey(pubkey);
|
||||||
}
|
}
|
||||||
@ -53,7 +51,7 @@ const authorize = async (pubkey) => {
|
|||||||
// Create user
|
// Create user
|
||||||
if (profile) {
|
if (profile) {
|
||||||
const fields = await findKind0Fields(profile);
|
const fields = await findKind0Fields(profile);
|
||||||
const payload = { pubkey, username: fields.username, avatar: fields.avatar };
|
const payload = { pubkey, username: fields.username, avatar: fields.avatar, name: fields.username };
|
||||||
|
|
||||||
if (appConfig.authorPubkeys.includes(pubkey)) {
|
if (appConfig.authorPubkeys.includes(pubkey)) {
|
||||||
// create a new author role for this user
|
// create a new author role for this user
|
||||||
@ -168,8 +166,32 @@ export const authOptions = {
|
|||||||
// Create new user if not exists
|
// Create new user if not exists
|
||||||
dbUser = await createUser({
|
dbUser = await createUser({
|
||||||
pubkey: pubkey,
|
pubkey: pubkey,
|
||||||
username: pubkey.slice(0, 8), // Use first 8 characters of pubkey as username
|
username: pubkey.slice(0, 8),
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// Check if this user has a linked GitHub account
|
||||||
|
const githubAccount = await prisma.account.findFirst({
|
||||||
|
where: {
|
||||||
|
userId: dbUser.id,
|
||||||
|
provider: 'github'
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
user: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (githubAccount) {
|
||||||
|
// Return the user with GitHub provider information
|
||||||
|
return {
|
||||||
|
...dbUser,
|
||||||
|
pubkey,
|
||||||
|
privkey,
|
||||||
|
// Add these fields to switch to GitHub provider
|
||||||
|
provider: 'github',
|
||||||
|
type: 'oauth',
|
||||||
|
providerAccountId: githubAccount.providerAccountId
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return user object with pubkey and privkey
|
// Return user object with pubkey and privkey
|
||||||
@ -179,11 +201,23 @@ export const authOptions = {
|
|||||||
],
|
],
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async jwt({ token, user, account, trigger, profile }) {
|
async jwt({ token, user, account, trigger, profile }) {
|
||||||
// Add account and profile to token if they exist
|
if (user?.provider === 'github') {
|
||||||
if (account) {
|
// User has a linked GitHub account, use that as the primary provider
|
||||||
token.account = account;
|
token.account = {
|
||||||
|
provider: 'github',
|
||||||
|
type: 'oauth',
|
||||||
|
providerAccountId: user.providerAccountId
|
||||||
|
};
|
||||||
|
// Add GitHub profile information
|
||||||
|
token.githubProfile = {
|
||||||
|
login: user.username,
|
||||||
|
avatar_url: user.avatar,
|
||||||
|
email: user.email
|
||||||
|
};
|
||||||
|
} else if (account) {
|
||||||
// Store GitHub-specific information
|
// Store GitHub-specific information
|
||||||
if (account.provider === 'github') {
|
if (account.provider === 'github') {
|
||||||
|
token.account = account;
|
||||||
token.githubProfile = {
|
token.githubProfile = {
|
||||||
login: profile?.login,
|
login: profile?.login,
|
||||||
avatar_url: profile?.avatar_url,
|
avatar_url: profile?.avatar_url,
|
||||||
@ -246,6 +280,22 @@ export const authOptions = {
|
|||||||
user.email = token.githubProfile?.email;
|
user.email = token.githubProfile?.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (account && account.provider === "github" && user?.id && user?.pubkey) {
|
||||||
|
// we are linking a github account to an existing account
|
||||||
|
const updatedUser = await updateUser(user.id, {
|
||||||
|
name: profile?.login,
|
||||||
|
username: profile?.login,
|
||||||
|
avatar: profile?.avatar_url,
|
||||||
|
email: profile?.email,
|
||||||
|
privkey: user.privkey || token.privkey, // Preserve the existing privkey
|
||||||
|
image: profile?.avatar_url, // Also save to image field
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updatedUser) {
|
||||||
|
user = await getUserById(user.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
token.user = user;
|
token.user = user;
|
||||||
if (user.pubkey && user.privkey) {
|
if (user.pubkey && user.privkey) {
|
||||||
@ -269,9 +319,10 @@ export const authOptions = {
|
|||||||
// Override only the GitHub-specific fields
|
// Override only the GitHub-specific fields
|
||||||
session.user = {
|
session.user = {
|
||||||
...dbUser, // This includes role, purchases, userCourses, userLessons, etc.
|
...dbUser, // This includes role, purchases, userCourses, userLessons, etc.
|
||||||
username: token.githubProfile.login,
|
username: token.githubProfile?.login,
|
||||||
avatar: token.githubProfile.avatar_url,
|
name: token.githubProfile?.login,
|
||||||
email: token.githubProfile.email
|
avatar: token.githubProfile?.avatar_url,
|
||||||
|
email: token.githubProfile?.email
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// For non-GitHub sessions, use the existing token.user
|
// For non-GitHub sessions, use the existing token.user
|
||||||
|
Loading…
x
Reference in New Issue
Block a user