-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): add repo onboarding UI to fetch all user repos from GitHub
- Loading branch information
Showing
29 changed files
with
1,149 additions
and
172 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
/node_modules | ||
/node_modules | ||
test/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
"use server"; | ||
|
||
import axios from "axios"; | ||
import { getAccessToken } from "@/db/user"; | ||
import { formatTimeAgo } from "@/lib/time"; | ||
import { Repo } from "@/types/gh"; | ||
|
||
export async function getRepos(github_id: number) { | ||
if (!github_id || github_id == -1) return []; | ||
const access_token = await getAccessToken(github_id); | ||
if (!access_token) return []; | ||
|
||
try { | ||
const res = await axios.get( | ||
`https://api.github.com/user/repos?visibility=all&sort=updated`, | ||
{ | ||
headers: { | ||
Authorization: `token ${access_token}`, | ||
Accept: "application/vnd.github.v3+json", | ||
}, | ||
}, | ||
); | ||
|
||
return (res.data as Repo[]) | ||
.map((i: any) => ({ | ||
name: i.name, | ||
last_updated: formatTimeAgo(new Date(i.updated_at)), | ||
private: i.private, | ||
owner: i.owner.login.toLowerCase(), | ||
})) | ||
.slice(0, 10); | ||
} catch (error) { | ||
console.log(error); | ||
return []; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { NextApiRequest, NextApiResponse } from "next"; | ||
|
||
type ResponseData = { | ||
message: string; | ||
}; | ||
|
||
export default function handler( | ||
req: NextApiRequest, | ||
res: NextApiResponse<ResponseData>, | ||
) { | ||
res.status(200).json({ message: "Hello from Next.js!" }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,30 @@ | ||
import ProtectRoute from "@/components/ProtectRoute"; | ||
import React from "react"; | ||
"use client"; | ||
|
||
export default async function Dashboard() { | ||
return (await ProtectRoute()) ?? <div>page</div>; | ||
import { useEffect, useState } from "react"; | ||
import { useSession } from "next-auth/react"; | ||
import { getRepos } from "@/actions/gh/repos"; | ||
import { Repo } from "@/types/gh"; | ||
import ProtectClientRoute from "@/components/ProtectRoute/ProtectClientRoute"; | ||
import Onboarding from "@/components/Dash/OnBoarding"; | ||
|
||
export default function DashBoard() { | ||
const { data: session } = useSession(); | ||
const protectRoute = ProtectClientRoute(); | ||
const [repos, setRepos] = useState<Repo[]>([]); | ||
const [loading, setLoading] = useState(false); | ||
|
||
useEffect(() => { | ||
(async () => { | ||
setLoading(true); | ||
setRepos( | ||
await getRepos(session?.user.github_id ?? -1).finally(() => { | ||
setLoading(false); | ||
}), | ||
); | ||
})(); | ||
}, [session?.user.github_id]); | ||
|
||
if (protectRoute) return protectRoute; | ||
|
||
return <Onboarding repos={repos} loading={loading} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
"use client"; | ||
|
||
import { Button } from "@/components/ui/button"; | ||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; | ||
import { Input } from "@/components/ui/input"; | ||
import { | ||
Select, | ||
SelectContent, | ||
SelectItem, | ||
SelectTrigger, | ||
SelectValue, | ||
} from "@/components/ui/select"; | ||
import { LockIcon, SearchIcon } from "lucide-react"; | ||
import Image from "next/image"; | ||
import { Repo } from "@/types/gh"; | ||
import Link from "next/link"; | ||
import { Skeleton } from "@/components/ui/skeleton"; | ||
|
||
export default function Onboarding({ | ||
repos, | ||
loading, | ||
}: { | ||
repos: Repo[]; | ||
loading: boolean; | ||
}) { | ||
return ( | ||
<div className="flex h-full w-full flex-col items-center justify-center bg-black p-8 text-white"> | ||
<div className="mx-auto max-w-6xl"> | ||
<h1 className="mb-2 text-4xl font-bold"> | ||
Let's build something new. | ||
</h1> | ||
<p className="mb-8 text-gray-400"> | ||
To deploy a new Project, select the repository you want to deploy. The | ||
repository must be hosted on GitHub. | ||
</p> | ||
<div className="flex flex-col gap-8 lg:flex-row"> | ||
<Card className="w-full border-gray-800 bg-gray-900 lg:w-1/2"> | ||
<CardHeader> | ||
<CardTitle>Import Git Repository</CardTitle> | ||
</CardHeader> | ||
<CardContent> | ||
<div className="mb-4 flex gap-2"> | ||
<Select> | ||
<SelectTrigger className="w-[180px] border-gray-700 bg-gray-800"> | ||
<SelectValue placeholder="pulkitxm2" /> | ||
</SelectTrigger> | ||
<SelectContent> | ||
<SelectItem value="pulkitxm2">pulkitxm2</SelectItem> | ||
</SelectContent> | ||
</Select> | ||
<div className="relative flex-grow"> | ||
<SearchIcon className="absolute left-3 top-1/2 size-2 -translate-y-1/2 transform text-gray-400" /> | ||
<Input | ||
className="w-full border-gray-700 bg-gray-800 pl-10" | ||
placeholder="Search..." | ||
/> | ||
</div> | ||
</div> | ||
<div className="h-[200px] max-h-[300px] overflow-y-auto p-2 px-5"> | ||
{loading | ||
? Array.from({ length: 3 }).map((_, index) => ( | ||
<div | ||
key={index} | ||
className="flex items-center justify-between border-b border-gray-700 py-3" | ||
> | ||
<div className="flex items-center gap-3"> | ||
<Skeleton className="h-8 w-8 rounded-full" /> | ||
<div className="flex flex-col"> | ||
<Skeleton className="h-4 w-32 rounded" /> | ||
<Skeleton className="mt-1 h-3 w-20 rounded" /> | ||
</div> | ||
</div> | ||
<Skeleton className="h-8 w-24 rounded" /> | ||
</div> | ||
)) | ||
: repos.map((repo, index) => ( | ||
<div | ||
key={index} | ||
className="flex items-center justify-between border-b border-gray-800 py-3" | ||
> | ||
<div className="flex items-center gap-3"> | ||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-700 uppercase text-gray-300"> | ||
{repo.name[0]} | ||
</div> | ||
<div> | ||
<div className="flex items-center"> | ||
<Link | ||
href={`https://github.com/${repo.owner}/${repo.name}`.toLowerCase()} | ||
target="_blank" | ||
rel="noreferrer" | ||
className="hover:underline" | ||
> | ||
{repo.name} | ||
</Link> | ||
{repo.private && ( | ||
<LockIcon className="ml-1 h-4 w-4" /> | ||
)} | ||
</div> | ||
<p className="text-sm text-gray-400"> | ||
{repo.last_updated} | ||
</p> | ||
</div> | ||
</div> | ||
<Button | ||
variant="outline" | ||
className="border-gray-600 bg-transparent hover:bg-gray-800" | ||
> | ||
Import | ||
</Button> | ||
</div> | ||
))} | ||
</div> | ||
<Button variant="link" className="mt-4 text-blue-400"> | ||
Import Third-Party Git Repository → | ||
</Button> | ||
</CardContent> | ||
</Card> | ||
<div className="relative w-full lg:w-1/2"> | ||
<Image | ||
className="w-full" | ||
alt="Onboarding" | ||
src="https://storage.googleapis.com/devitary-image-host.appspot.com/15848031292911696601-undraw_designer_life_w96d.svg" | ||
fill | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file was deleted.
Oops, something went wrong.
2 changes: 1 addition & 1 deletion
2
core/components/LoginWithGithub.tsx → ...mponents/ProtectRoute/LoginWithGithub.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { useSession } from "next-auth/react"; | ||
import ProtectRouteUI from "../ProtectRoute"; | ||
|
||
export default function ProtectClientRoute() { | ||
const { data: session } = useSession(); | ||
if (!session) { | ||
return <ProtectRouteUI />; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { getServerSession } from "next-auth"; | ||
import { authOptions } from "@/lib/options"; | ||
import ProtectRouteUI from "../ProtectRoute"; | ||
|
||
export async function ProtectServerRoute() { | ||
const session = await getServerSession(authOptions); | ||
if (!session) { | ||
return <ProtectRouteUI />; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { | ||
Card, | ||
CardContent, | ||
CardDescription, | ||
CardHeader, | ||
CardTitle, | ||
} from "@/components/ui/card"; | ||
import LoginWithGithub from "./LoginWithGithub"; | ||
|
||
export default function ProtectRouteUI() { | ||
return ( | ||
<div className="flex h-full w-full items-center justify-center text-white"> | ||
<Card className="w-[350px]"> | ||
<CardHeader> | ||
<CardTitle>Unauthorized Access</CardTitle> | ||
<CardDescription> | ||
You are not authorized to view this page. | ||
</CardDescription> | ||
</CardHeader> | ||
<CardContent> | ||
<LoginWithGithub /> | ||
</CardContent> | ||
</Card> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import * as React from "react"; | ||
import { cva, type VariantProps } from "class-variance-authority"; | ||
|
||
import { cn } from "@/lib/utils"; | ||
|
||
const badgeVariants = cva( | ||
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", | ||
{ | ||
variants: { | ||
variant: { | ||
default: | ||
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", | ||
secondary: | ||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", | ||
destructive: | ||
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", | ||
outline: "text-foreground", | ||
}, | ||
}, | ||
defaultVariants: { | ||
variant: "default", | ||
}, | ||
}, | ||
); | ||
|
||
export interface BadgeProps | ||
extends React.HTMLAttributes<HTMLDivElement>, | ||
VariantProps<typeof badgeVariants> {} | ||
|
||
function Badge({ className, variant, ...props }: BadgeProps) { | ||
return ( | ||
<div className={cn(badgeVariants({ variant }), className)} {...props} /> | ||
); | ||
} | ||
|
||
export { Badge, badgeVariants }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import * as React from "react"; | ||
|
||
import { cn } from "@/lib/utils"; | ||
|
||
export interface InputProps | ||
extends React.InputHTMLAttributes<HTMLInputElement> {} | ||
|
||
const Input = React.forwardRef<HTMLInputElement, InputProps>( | ||
({ className, type, ...props }, ref) => { | ||
return ( | ||
<input | ||
type={type} | ||
className={cn( | ||
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", | ||
className, | ||
)} | ||
ref={ref} | ||
{...props} | ||
/> | ||
); | ||
}, | ||
); | ||
Input.displayName = "Input"; | ||
|
||
export { Input }; |
Oops, something went wrong.