mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-04-19 19:01:19 +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);
|
||||
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 activityCount = contributionData[dateString] || 0;
|
||||
|
||||
// Debug log
|
||||
if (activityCount > 0) {
|
||||
console.log('Found activity:', {
|
||||
date: currentDate.toDateString(),
|
||||
dateString,
|
||||
activityCount
|
||||
});
|
||||
}
|
||||
|
||||
calendar[weekDay].push({
|
||||
date: new Date(currentDate),
|
||||
count: activityCount
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { ProgressBar } from 'primereact/progressbar';
|
||||
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 = [
|
||||
{
|
||||
@ -18,11 +19,10 @@ const allTasks = [
|
||||
status: 'PlebDevs Starter',
|
||||
completed: false,
|
||||
tier: 'New Dev',
|
||||
courseId: "f538f5c5-1a72-4804-8eb1-3f05cea64874",
|
||||
// courseId: "f538f5c5-1a72-4804-8eb1-3f05cea64874",
|
||||
courseId: "f6daa88a-53d6-4901-8dbd-d2203a05b7ab",
|
||||
subTasks: [
|
||||
{ status: 'Connect GitHub', completed: false },
|
||||
{ status: 'Create First GitHub Repo', completed: false },
|
||||
{ status: 'Push Commit', completed: false }
|
||||
{ status: 'Complete the course', completed: false },
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -32,7 +32,7 @@ const allTasks = [
|
||||
courseId: 'f73c37f4-df2e-4f7d-a838-dce568c76136',
|
||||
subTasks: [
|
||||
{ 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',
|
||||
subTasks: [
|
||||
{ status: 'Complete the course', completed: false },
|
||||
{ status: 'Submit Link to completed project', completed: false },
|
||||
{ status: 'Select your completed project', completed: false },
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
const UserProgress = () => {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [currentTier, setCurrentTier] = useState('Pleb');
|
||||
const [currentTier, setCurrentTier] = useState(null);
|
||||
const [expandedItems, setExpandedItems] = useState({});
|
||||
const [completedCourses, setCompletedCourses] = useState([]);
|
||||
const [tasks, setTasks] = useState([]);
|
||||
|
||||
const { data: session } = useSession();
|
||||
const { data: session, update } = useSession();
|
||||
|
||||
useEffect(() => {
|
||||
if (session?.user) {
|
||||
@ -74,20 +74,37 @@ const UserProgress = () => {
|
||||
}, [session]);
|
||||
|
||||
const generateTasks = (completedCourseIds) => {
|
||||
const updatedTasks = allTasks.map(task => ({
|
||||
...task,
|
||||
completed: task.courseId === null || completedCourseIds.includes(task.courseId),
|
||||
subTasks: task.subTasks ? task.subTasks.map(subTask => ({
|
||||
...subTask,
|
||||
completed: completedCourseIds.includes(task.courseId)
|
||||
})) : undefined
|
||||
}));
|
||||
const updatedTasks = allTasks.map(task => {
|
||||
if (task.status === 'Connect GitHub') {
|
||||
return {
|
||||
...task,
|
||||
completed: session?.githubProfile ? true : false,
|
||||
subTasks: task.subTasks ? task.subTasks.map(subTask => ({
|
||||
...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);
|
||||
};
|
||||
|
||||
const calculateProgress = (completedCourseIds) => {
|
||||
let progressValue = 25;
|
||||
let progressValue = 0;
|
||||
|
||||
if (session?.githubProfile) {
|
||||
progressValue += 25;
|
||||
}
|
||||
|
||||
const remainingTasks = allTasks.slice(1);
|
||||
remainingTasks.forEach(task => {
|
||||
@ -100,16 +117,16 @@ const UserProgress = () => {
|
||||
};
|
||||
|
||||
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")) {
|
||||
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);
|
||||
@ -122,6 +139,12 @@ const UserProgress = () => {
|
||||
}));
|
||||
};
|
||||
|
||||
const handleGitHubLink = async () => {
|
||||
if (!session?.user?.id) return;
|
||||
|
||||
await signIn("github");
|
||||
};
|
||||
|
||||
return (
|
||||
<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>
|
||||
@ -139,30 +162,92 @@ const UserProgress = () => {
|
||||
|
||||
<div className="mb-6">
|
||||
<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>
|
||||
|
||||
<ul className="space-y-4 mb-6">
|
||||
{tasks.map((task, index) => (
|
||||
console.log(task),
|
||||
<li key={index}>
|
||||
<div className="flex items-center justify-between">
|
||||
<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>
|
||||
<Accordion
|
||||
activeIndex={expandedItems[index] ? 0 : null}
|
||||
onTabChange={(e) => handleAccordionChange(index, e.index === 0)}
|
||||
>
|
||||
<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 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>
|
||||
)}
|
||||
<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>
|
||||
{task.subTasks && (
|
||||
<ul className="space-y-2">
|
||||
{task.subTasks.map((subTask, subIndex) => (
|
||||
<li key={subIndex} className="flex items-center">
|
||||
{subTask.completed ? (
|
||||
<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>
|
||||
))}
|
||||
</ul>
|
||||
|
@ -12,8 +12,6 @@ import { createRole } from "@/db/models/roleModels";
|
||||
import appConfig from "@/config/appConfig";
|
||||
import GithubProvider from "next-auth/providers/github";
|
||||
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?
|
||||
|
||||
@ -40,7 +38,7 @@ const authorize = async (pubkey) => {
|
||||
dbUser = await getUserByPubkey(pubkey);
|
||||
}
|
||||
} 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) {
|
||||
dbUser = await getUserByPubkey(pubkey);
|
||||
}
|
||||
@ -53,7 +51,7 @@ const authorize = async (pubkey) => {
|
||||
// Create user
|
||||
if (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)) {
|
||||
// create a new author role for this user
|
||||
@ -168,8 +166,32 @@ export const authOptions = {
|
||||
// Create new user if not exists
|
||||
dbUser = await createUser({
|
||||
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
|
||||
@ -179,11 +201,23 @@ export const authOptions = {
|
||||
],
|
||||
callbacks: {
|
||||
async jwt({ token, user, account, trigger, profile }) {
|
||||
// Add account and profile to token if they exist
|
||||
if (account) {
|
||||
token.account = account;
|
||||
if (user?.provider === 'github') {
|
||||
// User has a linked GitHub account, use that as the primary provider
|
||||
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
|
||||
if (account.provider === 'github') {
|
||||
token.account = account;
|
||||
token.githubProfile = {
|
||||
login: profile?.login,
|
||||
avatar_url: profile?.avatar_url,
|
||||
@ -246,6 +280,22 @@ export const authOptions = {
|
||||
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) {
|
||||
token.user = user;
|
||||
if (user.pubkey && user.privkey) {
|
||||
@ -269,9 +319,10 @@ export const authOptions = {
|
||||
// Override only the GitHub-specific fields
|
||||
session.user = {
|
||||
...dbUser, // This includes role, purchases, userCourses, userLessons, etc.
|
||||
username: token.githubProfile.login,
|
||||
avatar: token.githubProfile.avatar_url,
|
||||
email: token.githubProfile.email
|
||||
username: token.githubProfile?.login,
|
||||
name: token.githubProfile?.login,
|
||||
avatar: token.githubProfile?.avatar_url,
|
||||
email: token.githubProfile?.email
|
||||
};
|
||||
} else {
|
||||
// For non-GitHub sessions, use the existing token.user
|
||||
|
Loading…
x
Reference in New Issue
Block a user