Skip to content

Commit 62a3d07

Browse files
Merge pull request #6 from Web3-Plurality/new-developer-dashboard
New developer dashboard
2 parents 567219c + dcaab67 commit 62a3d07

78 files changed

Lines changed: 4795 additions & 1776 deletions

Some content is hidden

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

app/auth/page.tsx

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"use client"
2+
3+
import { useEffect, useState } from "react"
4+
import { useRouter } from "next/navigation"
5+
import axios from "axios"
6+
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
7+
import { Input } from "@/components/ui/input"
8+
import { Button } from "@/components/ui/button"
9+
import { Label } from "@/components/ui/label"
10+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
11+
import { Loader2 } from "lucide-react"
12+
13+
export default function AuthPage() {
14+
const [activeTab, setActiveTab] = useState("register")
15+
const [projectName, setProjectName] = useState("")
16+
const [website, setWebsite] = useState("")
17+
const [email, setEmail] = useState("")
18+
const [errors, setErrors] = useState<{ [key: string]: string }>({})
19+
const [isLoading, setIsLoading] = useState(false) // Added loading state
20+
const [stytchToken, setStytchToken] = useState<string | null>(null);
21+
const router = useRouter()
22+
23+
// if token is already have then redirect to dashboard
24+
useEffect(()=>{
25+
const token = localStorage.getItem("stytchToken")
26+
if (token) {
27+
router.push("/dashboard")
28+
}else{
29+
setIsLoading(false);
30+
}
31+
32+
},[])
33+
34+
useEffect(() => {
35+
setStytchToken(localStorage.getItem("stytchToken"));
36+
const editMode = localStorage.getItem('editMode')
37+
if(editMode){
38+
setActiveTab("login")
39+
}
40+
}, []);
41+
42+
const validateForm = () => {
43+
const newErrors: { [key: string]: string } = {}
44+
if (activeTab === "register") {
45+
if (!projectName) newErrors.projectName = "Project name is required"
46+
if (!website) {
47+
newErrors.website = "Website is required"
48+
} else if (!/^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/.test(website)) {
49+
newErrors.website = "Please enter a valid URL"
50+
}
51+
}
52+
if (!email) {
53+
newErrors.email = "Email is required"
54+
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
55+
newErrors.email = "Please enter a valid email"
56+
}
57+
setErrors(newErrors)
58+
return Object.keys(newErrors).length === 0
59+
}
60+
61+
const handleSubmit = async (e: React.FormEvent) => {
62+
e.preventDefault()
63+
if (validateForm()) {
64+
setIsLoading(true) // Set loading to true
65+
66+
const url = `${process.env.NEXT_PUBLIC_DASHBOARD_API}/crm/client/login`
67+
try {
68+
69+
const response = await axios.post(url, { email })
70+
71+
if (response?.data?.success) {
72+
if (activeTab === "register") {
73+
localStorage.setItem("website", website)
74+
localStorage.setItem("projectName", projectName)
75+
}
76+
localStorage.setItem("emailId", response.data.emailId)
77+
router.push("/otp")
78+
} else {
79+
console.log("Something went wrong with the request")
80+
setErrors({ ...errors, submit: "Login failed. Please try again." })
81+
}
82+
} catch (error) {
83+
console.error("Error during submission:", error)
84+
setErrors({ ...errors, submit: "An error occurred. Please try again." })
85+
} finally {
86+
setIsLoading(false) // Set loading to false
87+
}
88+
}
89+
}
90+
91+
if (!stytchToken && isLoading) {
92+
return (
93+
<div className="flex justify-center items-center h-screen">
94+
<Loader2 className="h-8 w-8 animate-spin" />
95+
</div>
96+
);
97+
}
98+
99+
return (
100+
<div className="flex items-center justify-center min-h-screen bg-gray-100">
101+
<Card className="w-[350px]">
102+
<CardHeader className="text-center">
103+
<div className="flex justify-center items-center space-x-2 cursor-pointer">
104+
<img src="/logo.png" alt="Logo" className="h-20" onClick={() => window.open("https://plurality.network", "_blank", "noopener,noreferrer")} />
105+
</div>
106+
<CardTitle>Developer Dashboard</CardTitle>
107+
</CardHeader>
108+
<CardContent>
109+
<Tabs value={activeTab} onValueChange={setActiveTab}>
110+
<TabsList className="grid w-full grid-cols-2">
111+
<TabsTrigger value="register">Register</TabsTrigger>
112+
<TabsTrigger value="login">Login</TabsTrigger>
113+
</TabsList>
114+
<TabsContent value="register">
115+
<form onSubmit={handleSubmit}>
116+
<div className="grid w-full items-center gap-4">
117+
<div className="flex flex-col space-y-1.5">
118+
<Label htmlFor="projectName">Name of your project</Label>
119+
<Input id="projectName" value={projectName} onChange={(e) => setProjectName(e.target.value)} />
120+
{errors.projectName && <span className="text-red-500 text-sm">{errors.projectName}</span>}
121+
</div>
122+
<div className="flex flex-col space-y-1.5">
123+
<Label htmlFor="website">Website</Label>
124+
<Input id="website" value={website} onChange={(e) => setWebsite(e.target.value)} />
125+
{errors.website && <span className="text-red-500 text-sm">{errors.website}</span>}
126+
</div>
127+
<div className="flex flex-col space-y-1.5">
128+
<Label htmlFor="email">Business Email</Label>
129+
<Input id="email" type="email" value={email} onChange={(e) => setEmail(e.target.value.trim())} />
130+
{errors.email && <span className="text-red-500 text-sm">{errors.email}</span>}
131+
</div>
132+
</div>
133+
</form>
134+
</TabsContent>
135+
<TabsContent value="login">
136+
<form onSubmit={handleSubmit}>
137+
<div className="grid w-full items-center gap-4">
138+
<div className="flex flex-col space-y-1.5">
139+
<Label htmlFor="loginEmail">Business Email</Label>
140+
<Input id="loginEmail" type="email" value={email} onChange={(e) => setEmail(e.target.value.trim())} />
141+
{errors.email && <span className="text-red-500 text-sm">{errors.email}</span>}
142+
</div>
143+
</div>
144+
</form>
145+
</TabsContent>
146+
</Tabs>
147+
{errors.submit && <div className="mt-4 text-red-500 text-sm">{errors.submit}</div>}
148+
</CardContent>
149+
<CardFooter className="flex justify-between">
150+
<Button type="submit" onClick={handleSubmit} disabled={isLoading}>
151+
{" "}
152+
{/* Updated button */}
153+
{isLoading ? "Processing..." : activeTab === "register" ? "Register" : "Login"}
154+
</Button>
155+
</CardFooter>
156+
</Card>
157+
</div>
158+
)
159+
}
160+

app/dashboard/page.tsx

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { PlusCircle, Loader2 } from "lucide-react"
88
import AppCard from "@/components/AppCard"
99
import { useAppStore } from "../store/useAppStore"
1010
import { selectProfileType } from "@/lib/orbis-utils"
11+
import { useAuth } from "@/contexts/AuthContext"
1112

1213

1314

@@ -19,6 +20,7 @@ interface ClientApp {
1920
id: string,
2021
clientSecret: string,
2122
streamId: string
23+
appName: string,
2224
}
2325

2426
export default function DashboardPage() {
@@ -31,6 +33,9 @@ export default function DashboardPage() {
3133
// Access and update the store
3234
const { app, setApp } = useAppStore();
3335

36+
37+
const { hideDashboard, logout } = useAuth()
38+
3439

3540
const getClientApps = async () => {
3641
const url = `${process.env.NEXT_PUBLIC_DASHBOARD_API}/crm/client`
@@ -66,7 +71,7 @@ export default function DashboardPage() {
6671
const stream_data = await selectProfileType(streamId);
6772

6873
// If the rows array is empty, we can set default values
69-
const { profile_name = '', description = '' } = stream_data.rows[0] || {};
74+
const { profile_name = '', description = '' } = stream_data?.rows[0] || {};
7075

7176
return {
7277
...app, // Retain the existing app properties
@@ -80,36 +85,30 @@ export default function DashboardPage() {
8085

8186
})
8287
);
83-
// console.log("UPDATED APPS => ",updatedApps);
84-
85-
// const stream_data=await selectProfileType("kjzl6kcym7w8y6hhihqpmq4ul15xjxpwgxgu3zgr02fce448rtqn1tmsyea3110")
86-
// console.log("Stream Data=> ", stream_data)
87-
// setClientApps(response.data.client.apps)
88-
8988
setClientApps(updatedApps);
9089
} else {
9190
setError("Unexpected data format received from server")
9291
}
9392
} catch (err) {
9493
setError("You are not authorize please login again")
9594
console.error("Error fetching client apps:", err)
96-
localStorage.clear();
97-
router.push("/")
95+
logout()
9896
} finally {
9997
setIsLoading(false)
10098
}
10199
}
102100

103101
useEffect(() => {
104102
getClientApps()
105-
}, []) //Fixed useEffect dependency issue
103+
hideDashboard()
104+
localStorage.removeItem("editMode")
105+
}, [])
106106

107-
108-
const logout = () => {
109-
localStorage.clear();
110-
router.push("/")
107+
const onEdit = (app: ClientApp) => {
108+
setApp(app)
109+
router.push(`/?id=${app.id}`)
111110
}
112-
111+
113112
if (isLoading) {
114113
return (
115114
<div className="flex justify-center items-center h-screen">
@@ -131,12 +130,9 @@ export default function DashboardPage() {
131130
<h1 className="text-3xl font-bold">Developer Dashboard</h1>
132131
</div>
133132
<div className="flex space-x-4">
134-
<Button onClick={() => router.push("/clientapp")} className="rounded-full flex items-center">
133+
<Button onClick={() => router.push("/")} className="rounded-full flex items-center">
135134
<PlusCircle className="mr-2 h-4 w-4" /> Add App
136135
</Button>
137-
<Button onClick={() => logout()} className="rounded-full flex items-center">
138-
Logout
139-
</Button>
140136
</div>
141137
</div>
142138

@@ -153,20 +149,10 @@ export default function DashboardPage() {
153149
</div>
154150
</div>
155151
</div>
156-
152+
157153
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
158154
{clientApps.map((app) => (
159-
<AppCard key={app.id} app={app} onEdit={() => {
160-
setApp({
161-
logo: app?.logo,
162-
profileName: app?.profileName,
163-
profileDescription: app?.profileDescription,
164-
domains: app?.domains,
165-
clientAppId: app?.id,
166-
streamId: app?.streamId
167-
})
168-
router.push(`/clientapp?id=${app.id}`)
169-
}} />
155+
<AppCard key={app.id} app={app} onEdit={() => onEdit(app)} />
170156
))}
171157
</div>
172158
</div>

app/globals.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
@tailwind utilities;
44

55
body {
6-
font-family: Arial, Helvetica, sans-serif;
6+
font-family: theme('fontFamily.sans');
7+
background-image: url('/app-bg.png');
8+
background-size: cover;
9+
background-repeat: no-repeat;
710
}
811

912
@layer utilities {

app/layout.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,44 @@
11
import { ProjectProvider } from "@/contexts/ProjectContext"
2-
import './globals.css'
2+
import { ThemeProvider } from "next-themes"
3+
import "./globals.css"
4+
import Navbar from "@/components/ui/navbar"
5+
import { Lexend } from 'next/font/google';
6+
import { AuthProvider } from "@/contexts/AuthContext";
7+
import { Suspense } from "react";
8+
9+
const lexend = Lexend({
10+
subsets: ['latin'],
11+
variable: '--font-lexend',
12+
});
313

414
export default function RootLayout({
515
children,
616
}: {
717
children: React.ReactNode
818
}) {
919
return (
10-
<html lang="en">
20+
<html lang="en" className={lexend.className} suppressHydrationWarning>
1121
<head>
1222
<title>Plurality Developer Dashboard</title>
1323
<link rel="icon" href="favicon.png" type="image/x-icon" />
1424
</head>
1525
<body>
16-
<ProjectProvider>{children}</ProjectProvider>
26+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
27+
<AuthProvider>
28+
<Navbar />
29+
<ProjectProvider>
30+
<Suspense fallback={<div className="flex justify-center items-center h-screen bg-white dark:bg-black">
31+
<div className="flex space-x-2">
32+
<div className="w-4 h-4 bg-teal-600 rounded-full animate-bounce [animation-delay:-0.3s]"></div>
33+
<div className="w-4 h-4 bg-teal-600 rounded-full animate-bounce [animation-delay:-0.15s]"></div>
34+
<div className="w-4 h-4 bg-teal-600 rounded-full animate-bounce"></div>
35+
</div>
36+
</div>}>
37+
{children}
38+
</Suspense>
39+
</ProjectProvider>
40+
</AuthProvider>
41+
</ThemeProvider>
1742
</body>
1843
</html>
1944
)

0 commit comments

Comments
 (0)