diff --git a/dev-share-ui/app/globals.css b/dev-share-ui/app/globals.css index 20b1c1d..7e971d0 100644 --- a/dev-share-ui/app/globals.css +++ b/dev-share-ui/app/globals.css @@ -77,6 +77,6 @@ @apply border-border; } body { - @apply bg-background text-foreground; + @apply bg-background text-foreground min-h-screen flex flex-col; } } diff --git a/dev-share-ui/app/layout.tsx b/dev-share-ui/app/layout.tsx index d5f74fa..ea070f7 100644 --- a/dev-share-ui/app/layout.tsx +++ b/dev-share-ui/app/layout.tsx @@ -3,12 +3,14 @@ import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; import { ThemeProvider } from '@/components/ThemeProvider'; import { Toaster } from 'sonner'; +import Navbar from '@/components/Navbar'; const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { title: 'blotz dev share', - description: 'A place to discover and share the best developer resources with the community', + description: + 'A place to discover and share the best developer resources with the community', }; export default function RootLayout({ @@ -25,10 +27,11 @@ export default function RootLayout({ enableSystem disableTransitionOnChange > - {children} + +
{children}
); -} \ No newline at end of file +} diff --git a/dev-share-ui/app/result/page.tsx b/dev-share-ui/app/result/page.tsx new file mode 100644 index 0000000..7cb5fc2 --- /dev/null +++ b/dev-share-ui/app/result/page.tsx @@ -0,0 +1,13 @@ +import SearchBar from '@/components/SearchBar'; +import ResourceTable from '@/components/ResourceTable'; + +export default function ResultPage() { + return ( +
+
+ +
+ +
+ ); +} diff --git a/dev-share-ui/app/search/page.tsx b/dev-share-ui/app/search/page.tsx index 4190e7b..ad888d4 100644 --- a/dev-share-ui/app/search/page.tsx +++ b/dev-share-ui/app/search/page.tsx @@ -1,32 +1,26 @@ -"use client"; +'use client'; -import { useState } from "react"; -import Navbar from "@/components/Navbar"; -import HeroSection from "@/components/HeroSection"; -import { mockResources } from "@/lib/data"; -import { Resource, VectorSearchResultDTO } from "@/lib/types"; -import ResourceCard from "@/components/ResourceCard"; -import FromAISearchResourceCard from "@/components/FromAISearchResourceCard"; -import{handleGlobalSearch,searchResources} from "@/services/search-service"; +import { useState } from 'react'; +import HeroSection from '@/components/HeroSection'; +import { mockResources } from '@/lib/data'; +import ResourceCard from '@/components/ResourceCard'; +import FromAISearchResourceCard from '@/components/FromAISearchResourceCard'; +import { handleGlobalSearch, searchResources } from '@/services/search-service'; export default function SearchPage() { const [resources, setResources] = useState(mockResources); const [isSearching, setIsSearching] = useState(false); - const [SearchQuery, setSearchQuery] = useState(""); - const [isAIFallback, setIsAIFallback] = useState(false); + const [SearchQuery, setSearchQuery] = useState(''); const topRelative = 6; - - const handleSearch = async (query: string) => { setIsSearching(true); setSearchQuery(query); setResources([]); - + const result = await searchResources(query); if (result.length === 0) { await handleGlobalSearch(); - setIsAIFallback(true); } else { setResources(result); } @@ -39,12 +33,16 @@ export default function SearchPage() { // 1. Send user action to your API // 2. Update resource in your database // 3. Use this data to improve recommendations - - setResources(prevResources => - prevResources.map(resource => { + + setResources((prevResources) => + prevResources.map((resource) => { if (resource.id === id) { if (action === 'like') { - return { ...resource, likes: resource.isLiked ? resource.likes - 1 : resource.likes + 1, isLiked: !resource.isLiked }; + return { + ...resource, + likes: resource.isLiked ? resource.likes - 1 : resource.likes + 1, + isLiked: !resource.isLiked, + }; } else { return { ...resource, isBookmarked: !resource.isBookmarked }; } @@ -53,37 +51,14 @@ export default function SearchPage() { }) ); }; - + const handleSearchSuggestion = (suggestion: string) => { handleSearch(suggestion); }; - return ( -
- +
- -
-
-
- - {isAIFallback ? "No direct results found. Showing AI suggestions instead." : `${resources.length} resources found`} -
- View All -
- { ( -
- {resources.sort((a, b) => b.likes - a.likes).map((resource, idx) => - resource.isAIGenerated ? ( - - ) : ( - - ) - )} -
- )} -
-
+ ); -} \ No newline at end of file +} diff --git a/dev-share-ui/app/share/page.tsx b/dev-share-ui/app/share/page.tsx index 554a7ab..81fbd4a 100644 --- a/dev-share-ui/app/share/page.tsx +++ b/dev-share-ui/app/share/page.tsx @@ -1,172 +1,9 @@ -"use client"; - -import { useState } from "react"; -import { useRouter } from "next/navigation"; -import { Link as LinkIcon, MessageSquare } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { toast } from "sonner"; -import Navbar from "@/components/Navbar"; -import { submitSharedResource} from "@/services/share-service"; -import { pollShareStatus } from "@/services/polling-service"; +import { ShareResourceForm } from '@/components/ShareResourceForm'; export default function ShareResourcePage() { - const [url, setUrl] = useState(""); - const [comment, setComment] = useState(""); - const [isSubmitting, setIsSubmitting] = useState(false); - const [urlError, setUrlError] = useState(""); - const router = useRouter(); - - const validateUrl = (url: string) => { - try { - new URL(url); - setUrlError(""); - return true; - } catch (e) { - setUrlError("Please enter a valid URL (including http:// or https://)"); - return false; - } - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (!validateUrl(url)) { - return; - } - - setIsSubmitting(true); - - // TODO: Integration point for AI processing - // 1. Send URL and prompt to backend - // 2. Backend will: - // - Scrape the URL for content - // - Use AI to generate title, description, and tags - // - Store the processed resource - // Example: - // const resource = await processResourceWithAI({ url, prompt }); - - // Simulate API call - //set the max processing time - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 6000); - - //TODO: Move this to "share" service - try { - const taskId = await submitSharedResource(url, comment); - toast.success("Processing resource...please check status later", { duration: 2500 }); - - const result = await pollShareStatus(taskId, { maxPolls: 5, interval: 3000 }); - - if (result === "success") { - toast.success("Resource processing completed!"); - } else if (result === "failed") { - toast.error("Resource processing failed."); - } else { - toast.error("Resource processing timed out."); - } - - router.push("/"); - } catch (err: any) { - if (err.name === "AbortError") { - toast.error("Request timed out after 5 seconds."); - } else { - toast.error("An unexpected error occurred."); - } - } - setIsSubmitting(false); - router.push("/"); - }; - return ( -
- -
- - - Share a Resource - - Share a valuable developer resource. Our AI will analyze it and add relevant details. - -
-
-
- -
- -
- - setUrl(e.target.value)} - className="pl-10 focus:ring-2 focus:ring-primary/30 hover:border-primary transition-all duration-200" - required - /> -
- {urlError &&

{urlError}

} -
-
- -