Skip to content

Commit

Permalink
Merge pull request #92 from deepraj21/main
Browse files Browse the repository at this point in the history
feat: added docs,directory and fixed auth
  • Loading branch information
deepraj21 authored Dec 10, 2024
2 parents e98b8f8 + f0fbffe commit ddf915f
Show file tree
Hide file tree
Showing 32 changed files with 1,342 additions and 881 deletions.
9 changes: 7 additions & 2 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import TermsAndConditions from './pages/TermsAndConditions';
import Feed from './pages/Feed';
import UserPosts from './pages/UserPosts';
import ShowPostByID from './components/Posts/ShowPostByID';
import ErrorPage from './pages/ErrorPage';
import Directory from './pages/Directory';
import Docs from './pages/Docs';

const App = () => {
return (
Expand All @@ -31,14 +34,16 @@ const App = () => {
<Route path="/user/:username" element={<Profile />} />
<Route path="/relations/:username" element={<Visualization />} />
<Route path="/feed" element={<Feed />} />
<Route path="/docs" element={<Docs />} />
<Route path="/directory" element={<Directory />} />
<Route path="/posts/:username" element={<UserPosts />} />
<Route path="/post/:postId" element={<ShowPostByID />} />
<Route path="/privacy-policy" element={<PrivacyPolicy/>} />
<Route path="/terms-and-conditions" element={<TermsAndConditions/>}/>
<Route path="*" element={<div>404</div>} />
<Route path="*" element={<ErrorPage/>} />
</Routes>
</Router>
<Toaster />
<Toaster richColors closeButton/>
</ThemeProvider>
);
};
Expand Down
39 changes: 29 additions & 10 deletions client/src/components/Auth/user-auth-form-login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import { Icons } from "@/components/ui/icons";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Eye, EyeOff } from 'lucide-react';

const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000';

export function UserAuthForm() {
const [isLoading, setIsLoading] = useState<boolean>(false);
const [username, setUsername] = useState<string>('');
const [password, setPassword] = useState<string>('');
const [showPassword, setShowPassword] = useState<boolean>(false);
const navigate = useNavigate();

async function onSubmit(event: React.SyntheticEvent) {
Expand Down Expand Up @@ -56,6 +58,10 @@ export function UserAuthForm() {
}
}

const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};

return (
<div className={cn("grid gap-6")}>
<form onSubmit={onSubmit}>
Expand All @@ -81,16 +87,29 @@ export function UserAuthForm() {
<Label className="sr-only" htmlFor="password">
Password
</Label>
<Input
id="password"
placeholder="Password"
type="password"
autoComplete="current-password"
disabled={isLoading}
value={password}
onChange={(e) => setPassword(e.target.value)}
className='dark:bg-zinc-900'
/>
<div className="relative">
<Input
id="password"
placeholder="Password"
type={showPassword ? "text" : "password"}
autoComplete="current-password"
disabled={isLoading}
value={password}
onChange={(e) => setPassword(e.target.value)}
className='dark:bg-zinc-900 pr-10'
/>
<button
type="button"
onClick={togglePasswordVisibility}
className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600"
>
{showPassword ? (
<EyeOff className="h-4 w-4" />
) : (
<Eye className="h-4 w-4" />
)}
</button>
</div>
</div>
<Button disabled={isLoading}>
{isLoading && (
Expand Down
15 changes: 15 additions & 0 deletions client/src/components/Directory/UserDirectory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Suspense } from 'react'
import UserDirectoryContent from './UserDirectoryContent'

const UserDirectory = () => {
return (
<div className="container mx-auto">
<h1 className="text-5xl mt-2 mb-3">Developer Directory</h1>
<Suspense fallback={<div>Loading...</div>}>
<UserDirectoryContent />
</Suspense>
</div>
)
}

export default UserDirectory
81 changes: 81 additions & 0 deletions client/src/components/Directory/UserDirectoryContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useState, useMemo } from 'react'
import { Input } from "@/components/ui/input"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Card, CardContent } from "@/components/ui/card"
import { groupUsersByFirstLetter, User } from './directoryUtils'

async function getUsers() {
const res = await fetch('http://127.0.0.1:5000/directory')
if (!res.ok) {
throw new Error('Failed to fetch users')
}
return res.json()
}

export default function UserDirectoryContent() {
const [users, setUsers] = useState<User[]>([])
const [searchTerm, setSearchTerm] = useState('')
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)

useMemo(() => {
getUsers()
.then(fetchedUsers => {
setUsers(fetchedUsers)
setIsLoading(false)
})
.catch(() => {
setError('Failed to fetch users. Please try again later.')
setIsLoading(false)
})
}, [])

const filteredAndGroupedUsers = useMemo(() => {
const filtered = users.filter(user =>
user.username.toLowerCase().includes(searchTerm.toLowerCase())
)
return groupUsersByFirstLetter(filtered)
}, [users, searchTerm])

if (isLoading) return (<div>Loading...</div>)
if (error) return <div className="text-red-500">{error}</div>

return (
<>
<Input
type="text"
placeholder="Search by username"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="mb-4"
/>
<ScrollArea className="h-[600px]">
{Object.entries(filteredAndGroupedUsers).map(([letter, groupUsers]) => (
<div key={letter} className="mb-8">
<h2 className="text-xl font-semibold mb-4">{letter}</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 gap-4">
{groupUsers.map(user => (
<Card key={user.username} className="overflow-hidden">
<CardContent className="p-4">
<div className="flex items-center space-x-4">
<Avatar>
<AvatarImage src={`/placeholder.svg?height=40&width=40&text=${user.username.slice(0, 2).toUpperCase()}`} alt={user.username} />
<AvatarFallback>{user.username.slice(0, 2).toUpperCase()}</AvatarFallback>
</Avatar>
<div className='overflow-x-auto'>
<a href={`/user/${user.username}`}><p className="font-medium">{user.username}</p></a>
<p className="text-sm text-gray-500">{user.email}</p>
</div>
</div>
</CardContent>
</Card>
))}
</div>
</div>
))}
</ScrollArea>
</>
)
}

15 changes: 15 additions & 0 deletions client/src/components/Directory/directoryUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface User {
username: string;
email: string;
}

export const groupUsersByFirstLetter = (users: User[]): Record<string, User[]> => {
return users.reduce((acc, user) => {
const firstLetter = user.username[0].toUpperCase();
if (!acc[firstLetter]) {
acc[firstLetter] = [];
}
acc[firstLetter].push(user);
return acc;
}, {} as Record<string, User[]>);
};
173 changes: 173 additions & 0 deletions client/src/components/DocsComponent/DocsComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@

import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"

const DocsComponent = () => {
return (
<div className="container mx-auto">
<h1 className="text-4xl mb-6">DevHub: Building Core Features</h1>
<Card className="mb-8">
<CardHeader>
<CardTitle>Overview</CardTitle>
<CardDescription>
DevHub is a robust platform designed to enhance collaboration and networking within a developer community.
</CardDescription>
</CardHeader>
<CardContent>
<p>This document details the development of three key features:</p>
<ol className="list-decimal list-inside mt-2">
<li><strong>Finding People</strong>: Utilizing tag-based and graph-based relationships.</li>
<li><strong>Recommendations</strong>: Providing personalized suggestions for users and projects.</li>
<li><strong>Visualization</strong>: Displaying user connections and project interactions visually.</li>
</ol>
</CardContent>
</Card>

<Accordion type="single" collapsible className="w-full">
<AccordionItem value="feature-1">
<AccordionTrigger>
<h2 className="text-2xl">Feature 1: Finding People</h2>
</AccordionTrigger>
<AccordionContent>
<h3 className="text-xl mb-2">Purpose</h3>
<p className="mb-4">To help users connect with individuals sharing similar interests, skills, or project goals.</p>

<h3 className="text-xl mb-2">Implementation</h3>
<p className="mb-2">We use Neo4j's graph database to explore connections and common interests. The implementation involves two main queries:</p>

<h4 className="text-lg mt-4 mb-2">Common Tags Between Users</h4>
<p className="mb-2">Matches users based on shared project tags using a breadth-first search (BFS) query.</p>
<pre className="border p-4 rounded-md overflow-x-auto max-w-[310px]">
<code className="overflow-x-auto">{`MATCH (u1:User)-[:CONNECTED_TO]->(:Project)-[:TAGGED_WITH]->(tag:Tag)<-[:TAGGED_WITH]-(:Project)<-[:CONNECTED_TO]-(u2:User)
WHERE u1.username = $username AND u1 <> u2
RETURN u2.username AS commonUser, collect(tag.name) AS commonTags`}</code>
</pre>

<h4 className="text-lg mt-4 mb-2">User Suggestions</h4>
<p className="mb-2">Combines friendship data and project participation to suggest potential connections, excluding existing friends.</p>
<pre className="border p-4 rounded-md overflow-x-auto max-w-[310px]">
<code className="overflow-x-auto">{`MATCH (u:User {username: $username})-[:FRIEND]-(friend)
WITH u, collect(friend.username) AS friends
MATCH (u:User {username: $username})
RETURN u.suggestions AS suggestions, friends`}</code>
</pre>

<h3 className="text-xl mt-6 mb-2">Tech Stack</h3>
<ul className="list-disc list-inside">
<li><strong>Neo4j</strong>: Handles graph-based queries and relationships.</li>
<li><strong>Flask API</strong>: Serves data to the frontend in JSON format.</li>
</ul>
</AccordionContent>
</AccordionItem>

<AccordionItem value="feature-2">
<AccordionTrigger>
<h2 className="text-2xl ">Feature 2: Recommendations</h2>
</AccordionTrigger>
<AccordionContent>
<h3 className="text-xl mb-2">Purpose</h3>
<p className="mb-4">Provide users with tailored suggestions for other developers, projects, or opportunities based on their activities and connections.</p>

<h3 className="text-xl mb-2">Implementation</h3>
<p>The recommendation system analyzes:</p>
<ul className="list-disc list-inside mb-4">
<li>Direct connections and mutual friends.</li>
<li>Tags on projects the user has interacted with.</li>
</ul>
<p>Outputs are filtered to exclude current friends to maintain relevance.</p>

<h3 className="text-xl mt-6 mb-2">Tech Stack</h3>
<ul className="list-disc list-inside">
<li><strong>Neo4j</strong>: For real-time relationship computations.</li>
<li><strong>Python</strong>: For backend logic.</li>
<li><strong>LangChain & GenAI</strong>: Enhance recommendation relevance by using AI to validate and optimize query results.</li>
</ul>
</AccordionContent>
</AccordionItem>

<AccordionItem value="feature-3">
<AccordionTrigger>
<h2 className="text-2xl ">Feature 3: Visualization of User Connections and Projects</h2>
</AccordionTrigger>
<AccordionContent>
<h3 className="text-xl mb-2">Purpose</h3>
<p className="mb-4">Provide a visual interface to display how users are interconnected through projects and shared tags.</p>

<h3 className="text-xl mb-2">Implementation</h3>
<p className="mb-2">Graph traversal generates data about direct and indirect relationships.</p>
<p className="mb-2">Example Query:</p>
<pre className="border p-4 rounded-md overflow-x-auto mb-4 max-w-[310px]">
<code>{`MATCH (u:User {username: $username})-[r1]->(n1)
OPTIONAL MATCH (n1)-[r2]->(n2)
RETURN ...`}</code>
</pre>

<h4 className="text-lg mb-2">Data Transformation</h4>
<p className="mb-4">Converts Neo4j query results into nodes and edges for frontend visualization.</p>

<h4 className="text-lg mb-2">Frontend Representation</h4>
<ul className="list-disc list-inside mb-4">
<li>Nodes represent users or projects.</li>
<li>Links represent relationships like "connected to" or "tagged with."</li>
</ul>

<h3 className="text-xl mt-6 mb-2">Tech Stack</h3>
<ul className="list-disc list-inside">
<li><strong>Neo4j</strong>: Graph database.</li>
<li><strong>D3.js</strong>: For interactive frontend visualization.</li>
</ul>
</AccordionContent>
</AccordionItem>

<AccordionItem value="challenges">
<AccordionTrigger>
<h2 className="text-2xl ">Challenges and Solutions</h2>
</AccordionTrigger>
<AccordionContent>
<ol className="list-decimal list-inside space-y-4">
<li>
<strong>Efficient Querying</strong>
<ul className="list-disc list-inside ml-6 mt-2">
<li><strong>Challenge</strong>: Scaling graph queries for large datasets.</li>
<li><strong>Solution</strong>: Optimized Cypher queries and added caching mechanisms.</li>
</ul>
</li>
<li>
<strong>Maintaining Relevance</strong>
<ul className="list-disc list-inside ml-6 mt-2">
<li><strong>Challenge</strong>: Ensuring recommendations align with user interests.</li>
<li><strong>Solution</strong>: Used AI models (LangChain) to validate relevance.</li>
</ul>
</li>
<li>
<strong>Real-Time Visualization</strong>
<ul className="list-disc list-inside ml-6 mt-2">
<li><strong>Challenge</strong>: Rendering complex graphs in real-time.</li>
<li><strong>Solution</strong>: Pre-processed data before sending it to the frontend to reduce rendering time.</li>
</ul>
</li>
</ol>
</AccordionContent>
</AccordionItem>
</Accordion>

<Card className="mt-8">
<CardHeader>
<CardTitle>Conclusion</CardTitle>
</CardHeader>
<CardContent>
<p>
DevHub's core features leverage a blend of advanced graph databases, AI models, and visualization libraries to create a seamless user experience. By focusing on connections, recommendations, and intuitive interfaces, DevHub stands out as a platform for collaboration and growth in the developer community.
</p>
</CardContent>
</Card>
</div>
)
}

export default DocsComponent
Loading

0 comments on commit ddf915f

Please sign in to comment.