Skip to content

Commit 01ccb2b

Browse files
2 parents b1f4751 + 8820d61 commit 01ccb2b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+5552
-654
lines changed

.gitignore

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ yarn-error.log*
2626

2727
# local env files
2828
.env*.local
29-
.env
29+
3030
# vercel
3131
.vercel
32+
.env
3233

3334
# typescript
3435
*.tsbuildinfo
3536
next-env.d.ts
36-
37+
.env
3738
# IDE
3839
\.idea/
3940
.vscode

app/api/comment/route.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import CommentService from '@/libs/supabase/services/comments'
2+
import { NextResponse } from 'next/server'
3+
import ProductsService from '@/libs/supabase/services/products'
4+
5+
export async function POST(request: Request) {
6+
const { user_id, comment, slug } = await request.json()
7+
const productsService = new ProductsService(true)
8+
const product = await productsService.getBySlug(slug)
9+
const commentService = new CommentService(true)
10+
const res = await commentService.insert({
11+
content: comment,
12+
user_id,
13+
product_id: product?.id as number,
14+
})
15+
const commentRes = await commentService.getById(res?.id as number)
16+
return NextResponse.json({ res: commentRes })
17+
}

app/globals.css

+17
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,27 @@
66
scroll-behavior: smooth;
77
}
88

9+
/* Customize scrollbar */
10+
*::-webkit-scrollbar {
11+
@apply w-2.5 h-2.5 bg-slate-900 duration-150;
12+
}
13+
14+
*::-webkit-scrollbar:hover {
15+
@apply bg-slate-800;
16+
}
17+
18+
*::-webkit-scrollbar-thumb {
19+
@apply bg-slate-700 rounded-full;
20+
}
21+
922
.custom-screen {
1023
@apply max-w-screen-xl mx-auto px-4 md:px-8;
1124
}
1225

1326
.container-custom-screen {
1427
@apply max-w-3xl mx-auto px-4 md:px-8;
1528
}
29+
30+
ul > li:last-child:last-of-type .comment-divided-bar {
31+
display: none;
32+
}

app/layout.tsx

+43-18
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,62 @@
1-
import Navbar from '@/components/ui/Navbar';
2-
import './globals.css';
3-
import { Inter } from 'next/font/google';
1+
import Navbar from '@/components/ui/Navbar'
2+
import './globals.css'
3+
import { Inter } from 'next/font/google'
44

5-
import SupabaseListener from '@/components/supabase/listener';
6-
import SupabaseProvider from '@/components/supabase/provider';
7-
import { createServerClient } from '@/libs/supabase/server';
8-
import type { Database } from '@/libs/database.types';
9-
import type { SupabaseClient } from '@supabase/auth-helpers-nextjs';
5+
import SupabaseListener from '@/components/supabase/listener'
6+
import SupabaseProvider from '@/components/supabase/provider'
7+
import { createServerClient } from '@/libs/supabase/server'
8+
import type { Database } from '@/libs/supabase/types'
9+
import type { SupabaseClient } from '@supabase/auth-helpers-nextjs'
10+
import Footer from '@/components/ui/Footer/Footer'
1011

11-
export type TypedSupabaseClient = SupabaseClient<Database>;
12+
export type TypedSupabaseClient = SupabaseClient<Database>
1213

13-
const inter = Inter({ subsets: ['latin'] });
14+
declare global {
15+
interface Window {
16+
usermavenQ: any // Replace 'any' with the appropriate type of 'usermavenQ'
17+
}
18+
}
19+
20+
const inter = Inter({ subsets: ['latin'] })
1421

1522
export const metadata = {
1623
title: 'Dev Hunt – The best new DevTools in tech.',
1724
description: '',
1825
}
1926

2027
// do not cache this layout
21-
export const revalidate = 0;
28+
export const revalidate = 0
2229

2330
export default async function RootLayout({ children }: { children: React.ReactNode }) {
24-
const supabase = createServerClient();
25-
const { data: { session } } = await supabase.auth.getSession();
31+
const supabase = createServerClient()
32+
const {
33+
data: { session },
34+
} = await supabase.auth.getSession()
35+
2636
return (
2737
<html lang="en" className="bg-slate-900">
38+
<head>
39+
{/* <script
40+
src="https://t.usermaven.com/lib.js"
41+
data-key="UMuqbdiCeT"
42+
data-tracking-host="https://events.usermaven.com"
43+
data-autocapture="true"
44+
data-privacy-policy="strict"
45+
defer
46+
></script>
47+
<script>
48+
window.usermaven = window.usermaven || (function()
49+
{(window.usermavenQ = window.usermavenQ || []).push(arguments)})
50+
</script> */}
51+
</head>
2852
<body className={inter.className}>
2953
<main>
30-
<SupabaseProvider session={session}>
31-
<SupabaseListener serverAccessToken={session?.access_token} />
32-
<Navbar />
33-
{children}
34-
</SupabaseProvider>
54+
<SupabaseProvider session={session}>
55+
<SupabaseListener serverAccessToken={session?.access_token} />
56+
<Navbar />
57+
{children}
58+
<Footer />
59+
</SupabaseProvider>
3560
</main>
3661
</body>
3762
</html>

app/login/page.tsx

+41-65
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,56 @@
1-
"use client";
1+
'use client'
22

3-
import { useSupabase } from "@/components/supabase/provider";
3+
import { IconGithub } from '@/components/Icons'
4+
import { useSupabase } from '@/components/supabase/provider'
5+
import Brand from '@/components/ui/Brand'
6+
import Button from '@/components/ui/Button/Button'
7+
import { useState } from 'react'
8+
9+
const getURL = () => {
10+
let url =
11+
process?.env?.NEXT_PUBLIC_SITE_URL ?? // Set this to your site URL in production env.
12+
process?.env?.NEXT_PUBLIC_VERCEL_URL ?? // Automatically set by Vercel.
13+
'http://localhost:3000/'
14+
// Make sure to include `https://` when not localhost.
15+
url = url.includes('http') ? url : `https://${url}`
16+
// Make sure to including trailing `/`.
17+
url = url.charAt(url.length - 1) === '/' ? url : `${url}/`
18+
return url
19+
}
420

521
export default function Login() {
6-
const { supabase } = useSupabase();
22+
const { supabase } = useSupabase()
23+
const [isLoad, setLoad] = useState(false)
724

825
const handleLogin = async () => {
26+
setLoad(true)
927
await supabase.auth.signInWithOAuth({
10-
provider: "github",
11-
});
12-
};
13-
14-
const handleLogout = async () => {
15-
await supabase.auth.signOut();
16-
};
28+
provider: 'github',
29+
options: {
30+
redirectTo: getURL(),
31+
},
32+
})
33+
}
1734

1835
return (
19-
<section className="mt-20 pb-10 space-y-20">
20-
<div className="container-custom-screen">
21-
<div className="flex justify-center">
22-
<button
36+
<section>
37+
<div className="h-screen px-4 w-full flex items-center justify-center">
38+
<div className="text-center">
39+
<div className="space-y-3">
40+
<Brand w="180" h="50" className="mx-auto" />
41+
<h1 className="text-slate-50 text-2xl font-semibold">Log in to your account</h1>
42+
<p className="text-slate-300">Join our friendly community discovering and sharing the latest Dev Tools.</p>
43+
</div>
44+
<Button
45+
isLoad={isLoad}
46+
child={<IconGithub />}
2347
onClick={handleLogin}
24-
className="px-3 bg-white flex items-center justify-center gap-x-3 py-2.5 border rounded-lg hover:bg-gray-50 duration-150 active:bg-gray-100"
48+
className="text-sm font-medium mt-8 mx-auto flex text-slate-800 bg-slate-50 hover:bg-slate-200 active:bg-slate-100"
2549
>
26-
<svg
27-
className="w-5 h-5"
28-
viewBox="0 0 48 48"
29-
fill="none"
30-
xmlns="http://www.w3.org/2000/svg"
31-
>
32-
<g clipPath="url(#clip0_910_21)">
33-
<path
34-
fillRule="evenodd"
35-
clipRule="evenodd"
36-
d="M24.0005 1C18.303 1.00296 12.7923 3.02092 8.45374 6.69305C4.11521 10.3652 1.23181 15.452 0.319089 21.044C-0.593628 26.636 0.523853 32.3684 3.47174 37.2164C6.41963 42.0643 11.0057 45.7115 16.4099 47.5059C17.6021 47.7272 18.0512 46.9883 18.0512 46.36C18.0512 45.7317 18.0273 43.91 18.0194 41.9184C11.3428 43.3608 9.93197 39.101 9.93197 39.101C8.84305 36.3349 7.26927 35.6078 7.26927 35.6078C5.09143 34.1299 7.43223 34.1576 7.43223 34.1576C9.84455 34.3275 11.1123 36.6194 11.1123 36.6194C13.2504 40.2667 16.7278 39.2116 18.0949 38.5952C18.3095 37.0501 18.9335 35.999 19.621 35.4023C14.2877 34.8017 8.68408 32.7548 8.68408 23.6108C8.65102 21.2394 9.53605 18.9461 11.156 17.2054C10.9096 16.6047 10.087 14.1785 11.3905 10.8829C11.3905 10.8829 13.4054 10.2427 17.9916 13.3289C21.9253 12.2592 26.0757 12.2592 30.0095 13.3289C34.5917 10.2427 36.6026 10.8829 36.6026 10.8829C37.9101 14.1706 37.0875 16.5968 36.8411 17.2054C38.4662 18.9464 39.353 21.2437 39.317 23.6187C39.317 32.7824 33.7015 34.8017 28.3602 35.3905C29.2186 36.1334 29.9856 37.5836 29.9856 39.8122C29.9856 43.0051 29.9578 45.5736 29.9578 46.36C29.9578 46.9962 30.391 47.7391 31.6071 47.5059C37.0119 45.7113 41.5984 42.0634 44.5462 37.2147C47.4941 32.3659 48.611 26.6326 47.6972 21.0401C46.7835 15.4476 43.8986 10.3607 39.5587 6.68921C35.2187 3.01771 29.7067 1.00108 24.0085 1H24.0005Z"
37-
fill="#191717"
38-
/>
39-
<path
40-
d="M9.08887 35.264C9.03721 35.3826 8.84645 35.4181 8.69146 35.3351C8.53646 35.2522 8.42122 35.098 8.47686 34.9755C8.5325 34.853 8.71928 34.8214 8.87428 34.9044C9.02927 34.9874 9.14848 35.1455 9.08887 35.264Z"
41-
fill="#191717"
42-
/>
43-
<path
44-
d="M10.0626 36.3428C9.98028 36.384 9.88612 36.3955 9.79622 36.3753C9.70632 36.3551 9.62629 36.3045 9.56979 36.2321C9.41479 36.0662 9.38298 35.837 9.50221 35.7342C9.62143 35.6315 9.83606 35.6789 9.99105 35.8449C10.146 36.0108 10.1818 36.24 10.0626 36.3428Z"
45-
fill="#191717"
46-
/>
47-
<path
48-
d="M11.0085 37.714C10.8614 37.8167 10.6111 37.714 10.472 37.5085C10.4335 37.4716 10.4029 37.4274 10.382 37.3785C10.3611 37.3297 10.3503 37.2771 10.3503 37.224C10.3503 37.1709 10.3611 37.1183 10.382 37.0694C10.4029 37.0205 10.4335 36.9763 10.472 36.9395C10.619 36.8407 10.8694 36.9395 11.0085 37.141C11.1476 37.3425 11.1516 37.6112 11.0085 37.714Z"
49-
fill="#191717"
50-
/>
51-
<path
52-
d="M12.2921 39.0417C12.161 39.1879 11.8947 39.1484 11.6761 38.9508C11.4575 38.7532 11.4059 38.4845 11.537 38.3423C11.6682 38.2 11.9344 38.2395 12.161 38.4331C12.3875 38.6268 12.4312 38.8994 12.2921 39.0417Z"
53-
fill="#191717"
54-
/>
55-
<path
56-
d="M14.0923 39.8162C14.0327 40.0019 13.7625 40.0849 13.4922 40.0059C13.222 39.9268 13.0432 39.7055 13.0948 39.5159C13.1465 39.3262 13.4207 39.2393 13.6949 39.3262C13.9691 39.4131 14.144 39.6226 14.0923 39.8162Z"
57-
fill="#191717"
58-
/>
59-
<path
60-
d="M16.0557 39.9505C16.0557 40.1442 15.8331 40.3101 15.547 40.3141C15.2608 40.318 15.0264 40.16 15.0264 39.9663C15.0264 39.7727 15.2489 39.6067 15.535 39.6028C15.8212 39.5988 16.0557 39.753 16.0557 39.9505Z"
61-
fill="#191717"
62-
/>
63-
<path
64-
d="M17.8838 39.6463C17.9196 39.8399 17.7208 40.0414 17.4347 40.0888C17.1486 40.1363 16.8982 40.0217 16.8624 39.832C16.8267 39.6423 17.0333 39.4368 17.3115 39.3855C17.5897 39.3341 17.848 39.4526 17.8838 39.6463Z"
65-
fill="#191717"
66-
/>
67-
</g>
68-
<defs>
69-
<clipPath id="clip0_910_21">
70-
<rect width="48" height="48" fill="white" />
71-
</clipPath>
72-
</defs>
73-
</svg>
7450
Continue with Github
75-
</button>
51+
</Button>
7652
</div>
7753
</div>
7854
</section>
79-
);
55+
)
8056
}

app/page.tsx

+23-18
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,38 @@ import Tags from '@/components/ui/ProductCard/Product.Tags'
44
import Title from '@/components/ui/ProductCard/Product.Title'
55
import Votes from '@/components/ui/ProductCard/Product.Votes'
66
import ProductCard from '@/components/ui/ProductCard/ProductCard'
7-
import { getTopProducts } from '@/libs/supabase/services/products';
7+
import ProductsService from '@/libs/supabase/services/products'
88

99
export default async function Home() {
10-
const products = await getTopProducts('votes_counter', false);
10+
const products = await new ProductsService(true).getTopProducts('votes_count', false)
1111

1212
return (
1313
<section className="max-w-4xl mt-20 mx-auto px-4 md:px-8">
1414
<h1 className="text-slate-50 text-lg font-semibold xl:px-4">Find your next favorite product</h1>
1515

1616
<div className="mt-10 mb-12">
1717
<ul className="divide-y divide-slate-800/60">
18-
{(products || []).map((product, idx) => (
19-
<li key={idx} className="py-3">
20-
<ProductCard href={'/product/' + product.slug}>
21-
<Logo src={product.logo_url || ''} alt={product.name} />
22-
<div className="space-y-1">
23-
<Name>{product.name}</Name>
24-
<Title className="line-clamp-1 sm:line-clamp-2">{product.slogan}</Title>
25-
{/*@ts-ignore*/}
26-
<Tags items={[product.product_pricing_types?.title || 'Free', ...product.product_categories.map(c => c.name)]} />
27-
</div>
28-
<div className="flex-1 self-center flex justify-end">
29-
<Votes count={product.votes_counter} />
30-
</div>
31-
</ProductCard>
32-
</li>
33-
))}
18+
{products &&
19+
products.map((product, idx) => (
20+
<li key={idx} className="py-3">
21+
<ProductCard href={'/tool/' + product.slug}>
22+
<Logo src={product.logo_url || ''} alt={product.name} />
23+
<div className="space-y-1">
24+
<Name>{product.name}</Name>
25+
<Title className="line-clamp-1 sm:line-clamp-2">{product.slogan}</Title>
26+
<Tags
27+
items={[
28+
product.product_pricing_types?.title || 'Free',
29+
...product.product_categories.map(c => c.name),
30+
]}
31+
/>
32+
</div>
33+
<div className="flex-1 self-center flex justify-end">
34+
<Votes count={product.votes_count} />
35+
</div>
36+
</ProductCard>
37+
</li>
38+
))}
3439
</ul>
3540
</div>
3641
</section>

0 commit comments

Comments
 (0)