Compare commits

...

3 Commits

9 changed files with 1306 additions and 206 deletions

View File

@ -14,6 +14,7 @@ import {
Bell, Bell,
Settings, Settings,
LogOut, LogOut,
FileText,
} from "lucide-react"; } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider"; import { useAppTheme } from "@/components/ThemeProvider";
import { ThemeToggle } from "@/components/ThemeToggle"; import { ThemeToggle } from "@/components/ThemeToggle";
@ -98,6 +99,19 @@ export function Header() {
<Calendar className="w-4 h-4 sm:w-5 sm:h-5" /> <Calendar className="w-4 h-4 sm:w-5 sm:h-5" />
<span className="hidden sm:inline">Book Appointment</span> <span className="hidden sm:inline">Book Appointment</span>
</Link> </Link>
<Link
href="/deliverables"
className={`flex items-center gap-1 sm:gap-2 px-2 sm:px-3 md:px-4 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${
pathname === "/deliverables"
? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
: isDark
? "text-gray-300 hover:bg-gray-800"
: "text-gray-600 hover:bg-gray-100"
}`}
>
<FileText className="w-4 h-4 sm:w-5 sm:h-5" />
<span className="hidden sm:inline">Documentation</span>
</Link>
</nav> </nav>
{/* Right Side Actions */} {/* Right Side Actions */}

View File

@ -12,6 +12,7 @@ import {
Menu, Menu,
X, X,
Heart, Heart,
FileText,
} from "lucide-react"; } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider"; import { useAppTheme } from "@/components/ThemeProvider";
import { useAuth } from "@/hooks/useAuth"; import { useAuth } from "@/hooks/useAuth";
@ -20,6 +21,7 @@ import { toast } from "sonner";
const navItems = [ const navItems = [
{ label: "Dashboard", icon: LayoutGrid, href: "/admin/dashboard" }, { label: "Dashboard", icon: LayoutGrid, href: "/admin/dashboard" },
{ label: "Book Appointment", icon: Calendar, href: "/admin/booking" }, { label: "Book Appointment", icon: Calendar, href: "/admin/booking" },
{ label: "Deliverables", icon: FileText, href: "/deliverables" },
]; ];
export default function SideNav() { export default function SideNav() {

View File

@ -1,6 +1,7 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Link from "next/link";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -8,6 +9,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Button } from "@/components/ui/button";
import { import {
Users, Users,
UserCheck, UserCheck,
@ -18,6 +20,7 @@ import {
TrendingUp, TrendingUp,
ArrowUpRight, ArrowUpRight,
ArrowDownRight, ArrowDownRight,
FileText,
} from "lucide-react"; } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider"; import { useAppTheme } from "@/components/ThemeProvider";
import { getAllUsers } from "@/lib/actions/auth"; import { getAllUsers } from "@/lib/actions/auth";
@ -242,6 +245,16 @@ export default function Dashboard() {
Here's an overview of your practice today Here's an overview of your practice today
</p> </p>
</div> </div>
<div className="flex items-center gap-3">
<Link href="/deliverables">
<Button
variant="outline"
className={`flex items-center gap-2 ${isDark ? "bg-gray-800 border-gray-700 text-gray-100 hover:bg-gray-700" : "bg-white border-gray-200 text-gray-900 hover:bg-gray-50"}`}
>
<FileText className="w-4 h-4" />
<span className="hidden sm:inline">Deliverables</span>
</Button>
</Link>
<Select value={timePeriod} onValueChange={setTimePeriod}> <Select value={timePeriod} onValueChange={setTimePeriod}>
<SelectTrigger className={`w-full sm:w-[200px] cursor-pointer ${isDark ? "bg-gray-800 border-gray-700 text-gray-100" : "bg-white border-gray-200 text-gray-900"}`}> <SelectTrigger className={`w-full sm:w-[200px] cursor-pointer ${isDark ? "bg-gray-800 border-gray-700 text-gray-100" : "bg-white border-gray-200 text-gray-900"}`}>
<SelectValue placeholder="Select period" /> <SelectValue placeholder="Select period" />
@ -253,6 +266,7 @@ export default function Dashboard() {
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
</div>
{loading ? ( {loading ? (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">

View File

@ -375,31 +375,12 @@ function LoginContent() {
{/* Heading */} {/* Heading */}
<h1 className="text-3xl font-bold bg-linear-to-r from-rose-600 via-pink-600 to-rose-600 bg-clip-text text-transparent mb-2"> <h1 className="text-3xl font-bold bg-linear-to-r from-rose-600 via-pink-600 to-rose-600 bg-clip-text text-transparent mb-2">
{step === "login" && "Welcome back"} {step === "login" && "Welcome back"}
{step === "signup" && "Create an account"}
{step === "verify" && "Verify your email"} {step === "verify" && "Verify your email"}
</h1> </h1>
{/* Subtitle */} {/* Subtitle */}
{step === "login" && ( {step === "login" && (
<p className={`mb-6 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}> <p className={`mb-6 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
New to Attune Heart Therapy?{" "} Sign in to access your admin dashboard
<Link
href="/signup"
className={`underline font-medium ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
>
Sign up
</Link>
</p>
)}
{step === "signup" && (
<p className={`mb-6 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
Already have an account?{" "}
<button
type="button"
onClick={() => setStep("login")}
className={`underline font-medium ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
>
Log in
</button>
</p> </p>
)} )}
{step === "verify" && registeredEmail && ( {step === "verify" && registeredEmail && (
@ -519,168 +500,6 @@ function LoginContent() {
</form> </form>
)} )}
{/* Signup Form */}
{step === "signup" && (
<form className="space-y-4" onSubmit={handleSignup}>
{/* First Name Field */}
<div className="space-y-2">
<label htmlFor="firstName" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
First Name *
</label>
<Input
id="firstName"
type="text"
placeholder="John"
value={signupData.first_name}
onChange={(e) => handleSignupChange("first_name", e.target.value)}
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'} ${errors.first_name ? 'border-red-500' : ''}`}
required
/>
{errors.first_name && (
<p className="text-sm text-red-500">{errors.first_name}</p>
)}
</div>
{/* Last Name Field */}
<div className="space-y-2">
<label htmlFor="lastName" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
Last Name *
</label>
<Input
id="lastName"
type="text"
placeholder="Doe"
value={signupData.last_name}
onChange={(e) => handleSignupChange("last_name", e.target.value)}
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'} ${errors.last_name ? 'border-red-500' : ''}`}
required
/>
{errors.last_name && (
<p className="text-sm text-red-500">{errors.last_name}</p>
)}
</div>
{/* Email Field */}
<div className="space-y-2">
<label htmlFor="signup-email" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
Email address *
</label>
<Input
id="signup-email"
type="email"
placeholder="Email address"
value={signupData.email}
onChange={(e) => handleSignupChange("email", e.target.value)}
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'} ${errors.email ? 'border-red-500' : ''}`}
required
/>
{errors.email && (
<p className="text-sm text-red-500">{errors.email}</p>
)}
</div>
{/* Phone Field */}
<div className="space-y-2">
<label htmlFor="phone" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
Phone Number (Optional)
</label>
<Input
id="phone"
type="tel"
placeholder="+1 (555) 123-4567"
value={signupData.phone_number || ""}
onChange={(e) => handleSignupChange("phone_number", e.target.value)}
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`}
/>
</div>
{/* Password Field */}
<div className="space-y-2">
<label htmlFor="signup-password" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
Password *
</label>
<div className="relative">
<Input
id="signup-password"
type={showPassword ? "text" : "password"}
placeholder="Password (min 8 characters)"
value={signupData.password}
onChange={(e) => handleSignupChange("password", e.target.value)}
className={`h-11 pr-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'} ${errors.password ? 'border-red-500' : ''}`}
required
/>
<Button
type="button"
variant="ghost"
size="icon"
onClick={() => setShowPassword(!showPassword)}
className={`absolute right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
aria-label={showPassword ? "Hide password" : "Show password"}
>
{showPassword ? (
<EyeOff className="w-5 h-5" />
) : (
<Eye className="w-5 h-5" />
)}
</Button>
</div>
{errors.password && (
<p className="text-sm text-red-500">{errors.password}</p>
)}
</div>
{/* Confirm Password Field */}
<div className="space-y-2">
<label htmlFor="signup-password2" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
Confirm Password *
</label>
<div className="relative">
<Input
id="signup-password2"
type={showPassword2 ? "text" : "password"}
placeholder="Confirm password"
value={signupData.password2}
onChange={(e) => handleSignupChange("password2", e.target.value)}
className={`h-11 pr-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'} ${errors.password2 ? 'border-red-500' : ''}`}
required
/>
<Button
type="button"
variant="ghost"
size="icon"
onClick={() => setShowPassword2(!showPassword2)}
className={`absolute right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
aria-label={showPassword2 ? "Hide password" : "Show password"}
>
{showPassword2 ? (
<EyeOff className="w-5 h-5" />
) : (
<Eye className="w-5 h-5" />
)}
</Button>
</div>
{errors.password2 && (
<p className="text-sm text-red-500">{errors.password2}</p>
)}
</div>
{/* Submit Button */}
<Button
type="submit"
disabled={registerMutation.isPending}
className="w-full h-12 text-base font-semibold bg-linear-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed mt-6"
>
{registerMutation.isPending ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
Creating account...
</>
) : (
"Sign up"
)}
</Button>
</form>
)}
{/* OTP Verification Form */} {/* OTP Verification Form */}
{step === "verify" && ( {step === "verify" && (
@ -775,17 +594,17 @@ function LoginContent() {
)} )}
</Button> </Button>
{/* Back to signup */} {/* Back to login */}
<div className="text-center"> <div className="text-center">
<button <button
type="button" type="button"
onClick={() => { onClick={() => {
setStep("signup"); setStep("login");
setOtpData({ email: "", otp: "" }); setOtpData({ email: "", otp: "" });
}} }}
className={`text-sm font-medium ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-600 hover:text-gray-700'}`} className={`text-sm font-medium ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-600 hover:text-gray-700'}`}
> >
Back to signup Back to login
</button> </button>
</div> </div>
</form> </form>

View File

@ -0,0 +1,311 @@
"use client";
import { Button } from "@/components/ui/button";
import { ArrowLeft, Heart } from "lucide-react";
import React from "react";
import ReactMarkdown, { Components } from "react-markdown";
import remarkGfm from "remark-gfm";
const ReadmePage = () => {
const readmeContent = `
## Attune Heart Therapy
Welcome to your Attune Heart Therapy platform! This documentation provides everything you need to understand and navigate the complete system, including the landing page, booking system, user/client dashboard, and admin dashboard.
---
## 📂 What's Included
Your Attune Heart Therapy platform includes a comprehensive system for managing therapy appointments and client interactions:
| Section | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------- |
| Landing Page | Public-facing homepage with navigation, services overview, and booking access |
| Booking System | User-friendly appointment booking flow where clients can request therapy sessions |
| User Dashboard | Client portal to view appointments, manage profile, and track booking status |
| Admin Dashboard | Administrative interface to manage appointments, view statistics, and schedule sessions |
---
## 🔐 Admin Dashboard Access
### Step 1: Navigate to Login
1. Go to your website's homepage
2. Click on the **"Admin Panel"** link in the footer (under Quick Links)
3. Or navigate directly to: \`https://attunehearttherapy.com/login\`
### Step 2: Login Credentials
**Email Address:** \`Hello@AttuneHeartTherapy.com\`
**Password:** \`G&n2S;ffTc8f\`
### Step 3: Access Dashboard
1. Enter your admin email address
2. Enter your password
3. Click **"Sign In"**
4. You will be automatically redirected to the Admin Dashboard
---
## 🔗 Quick Access Links
[Visit Attune Heart Therapy](https://attunehearttherapy.com/) - Official website
[Access Admin Dashboard](https://attunehearttherapy.com/login) - Login to manage your practice
[Book an Appointment](https://attunehearttherapy.com/book-now) - Client booking page
---
## 📞 Support & Contact
For technical assistance, questions, or issues:
**Email:** [info@BlackBusinessLabs.com](mailto:info@BlackBusinessLabs.com)
**Phone:** [(646) 895-4856](tel:+16468954856) - *CEO Tray Bailey's direct mobile*
---
*For questions or additional support, please contact Black Business Labs at the information provided above.*`;
const components: Components = {
h1: ({ node, ...props }) => (
<h1
style={{
fontSize: "2.2em",
fontWeight: "600",
marginTop: "1.2em",
marginBottom: "0.6em",
borderBottom: "1px solid #eaeaea",
paddingBottom: "0.3em",
}}
{...props}
/>
),
h2: ({ node, children, ...props }) => {
// Extract text content from children
const extractText = (child: any): string => {
if (typeof child === 'string') return child;
if (typeof child === 'number') return String(child);
if (React.isValidElement(child)) {
const childProps = child.props as any;
if (childProps?.children) {
return React.Children.toArray(childProps.children).map(extractText).join('');
}
}
return '';
};
const textContent = React.Children.toArray(children).map(extractText).join('');
// Check if this is the title heading
if (textContent.includes('Attune Heart Therapy - System Overview')) {
return (
<h2
style={{
fontSize: "1.8em",
fontWeight: "600",
marginTop: "1.2em",
marginBottom: "0.6em",
borderBottom: "1px solid #eaeaea",
paddingBottom: "0.3em",
display: "flex",
alignItems: "center",
gap: "0.5em",
}}
{...props}
>
<span>{children}</span>
</h2>
);
}
return (
<h2
style={{
fontSize: "1.8em",
fontWeight: "600",
marginTop: "1.2em",
marginBottom: "0.6em",
borderBottom: "1px solid #eaeaea",
paddingBottom: "0.3em",
}}
{...props}
>
{children}
</h2>
);
},
h3: ({ node, ...props }) => (
<h3
style={{
fontSize: "1.5em",
fontWeight: "600",
marginTop: "1.2em",
marginBottom: "0.6em",
}}
{...props}
/>
),
p: ({ node, ...props }) => (
<p style={{ marginBottom: "1.2em", lineHeight: "1.8" }} {...props} />
),
a: ({ node, ...props }) => (
<a
style={{ color: "#0366d6", textDecoration: "none", fontWeight: "500" }}
{...props}
/>
),
ul: ({ node, ...props }) => (
<ul
style={{
paddingLeft: "1.5em",
marginBottom: "1.2em",
listStyleType: "disc",
}}
{...props}
/>
),
ol: ({ node, ...props }) => (
<ol
style={{
paddingLeft: "1.5em",
marginBottom: "1.2em",
listStyleType: "decimal",
}}
{...props}
/>
),
li: ({ node, ...props }) => (
<li style={{ marginBottom: "0.4em" }} {...props} />
),
table: ({ node, ...props }) => (
<table
style={{
width: "100%",
borderCollapse: "collapse",
marginBottom: "1.2em",
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
border: "1px solid #dfe2e5",
}}
{...props}
/>
),
th: ({ node, ...props }) => (
<th
style={{
border: "1px solid #dfe2e5",
padding: "0.6em 0.8em",
textAlign: "left",
backgroundColor: "#f6f8fa",
fontWeight: "600",
}}
{...props}
/>
),
td: ({ node, ...props }) => (
<td
style={{
border: "1px solid #dfe2e5",
padding: "0.6em 0.8em",
textAlign: "left",
}}
{...props}
/>
),
pre: ({ node, children, ...props }) => (
<pre
style={{
backgroundColor: "#f6f8fa",
padding: "1em",
borderRadius: "6px",
overflowX: "auto",
fontSize: "0.9em",
lineHeight: "1.5",
}}
{...props}
>
{children}
</pre>
),
code: (props) => {
// Using `props: any` and casting to bypass TypeScript error with `inline` prop.
const {
node,
inline: isInline,
className,
children,
// Destructure known non-HTML props from react-markdown to prevent them from being spread onto the <code> tag
index,
siblingCount,
ordered,
checked,
style: _style, // if style is passed in props, avoid conflict with style object below
...htmlProps // Spread remaining props, assuming they are valid HTML attributes for <code>
} = props as any;
const codeStyleBase = {
fontFamily:
'SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace',
};
if (isInline) {
return (
<code
className={className}
style={{
...codeStyleBase,
backgroundColor: "rgba(27,31,35,0.07)", // Slightly adjusted for better visibility
padding: "0.2em 0.4em",
margin: "0 0.1em",
fontSize: "85%",
borderRadius: "3px",
}}
{...htmlProps}
>
{children}
</code>
);
}
// For block code (inside <pre>)
return (
<code
className={className} // className might contain "language-js" etc.
style={{
...codeStyleBase,
// Most styling for block code is handled by the <pre> wrapper
// However, ensure no extra padding/margin if pre handles it
padding: 0,
backgroundColor: "transparent", // Pre has the background
}}
{...htmlProps}
>
{children}
</code>
);
},
};
return (
<div className="container mx-auto px-4 py-8 max-w-4xl">
<div className="bg-white p-8 rounded-lg shadow-md">
<Button
className="bg-gray-100 hover:bg-gray-50 shadow-md text-black"
onClick={() => window.history.back()}
>
<ArrowLeft className="mr-2" />
</Button>
<ReactMarkdown components={components} remarkPlugins={[remarkGfm]}>
{readmeContent}
</ReactMarkdown>
</div>
</div>
);
};
export default ReadmePage;

View File

@ -0,0 +1,72 @@
"use client";
import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import Image from "next/image";
export default function SwotAnalysisPage() {
const [isClient, setIsClient] = useState(false);
// This ensures the PDF viewer only renders on the client side
useEffect(() => {
setIsClient(true);
}, []);
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-6">
<Link href="/docs">
<Button variant="outline" className="mb-4">
Back to Documentation
</Button>
</Link>
<h1 className="text-3xl font-bold mb-2">WODEY SWOT Analysis</h1>
<p className="text-gray-600 mb-6">
An in-depth analysis of Strengths, Weaknesses, Opportunities, and
Threats for the WODEY platform.
</p>
</div>
<div className="flex justify-end mb-4">
<a
href="/docs/SWOT-Analysis.pdf"
download="WODEY-SWOT-Analysis.pdf"
className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded shadow-md"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
Download PDF
</a>
</div>
{isClient ? (
<div className="w-full h-[calc(100vh-200px)] rounded-lg overflow-hidden">
<Image
src="/WODEY-SWOT-Analysis.jpg"
alt="SWOT Analysis"
width={600}
height={600}
className="w-full h-full object-contain"
/>
</div>
) : (
<div className="flex items-center justify-center w-full h-[calc(100vh-200px)] bg-gray-100 rounded-lg">
Loading viewer...
</div>
)}
</div>
);
}

View File

@ -55,7 +55,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
const validation = loginSchema.safeParse(loginData); const validation = loginSchema.safeParse(loginData);
if (!validation.success) { if (!validation.success) {
const firstError = validation.error.issues[0]; const firstError = validation.error.issues[0];
setError(firstError.message); toast.error(firstError.message);
return; return;
} }
@ -70,7 +70,6 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
} }
} catch (err) { } catch (err) {
const errorMessage = err instanceof Error ? err.message : "Login failed. Please try again."; const errorMessage = err instanceof Error ? err.message : "Login failed. Please try again.";
setError(errorMessage);
toast.error(errorMessage); toast.error(errorMessage);
} }
}; };
@ -83,7 +82,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
const validation = registerSchema.safeParse(signupData); const validation = registerSchema.safeParse(signupData);
if (!validation.success) { if (!validation.success) {
const firstError = validation.error.issues[0]; const firstError = validation.error.issues[0];
setError(firstError.message); toast.error(firstError.message);
return; return;
} }
@ -106,7 +105,6 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
} }
} catch (err) { } catch (err) {
const errorMessage = err instanceof Error ? err.message : "Signup failed. Please try again."; const errorMessage = err instanceof Error ? err.message : "Signup failed. Please try again.";
setError(errorMessage);
toast.error(errorMessage); toast.error(errorMessage);
} }
}; };
@ -163,11 +161,6 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
{/* Signup Form */} {/* Signup Form */}
{isSignup ? ( {isSignup ? (
<form className="space-y-4 sm:space-y-5 py-4 sm:py-6" onSubmit={handleSignup}> <form className="space-y-4 sm:space-y-5 py-4 sm:py-6" onSubmit={handleSignup}>
{error && (
<div className={`p-3 rounded-lg border ${isDark ? 'bg-red-900/20 border-red-800' : 'bg-red-50 border-red-200'}`}>
<p className={`text-sm ${isDark ? 'text-red-200' : 'text-red-800'}`}>{error}</p>
</div>
)}
{/* First Name Field */} {/* First Name Field */}
<div className="space-y-1.5 sm:space-y-2"> <div className="space-y-1.5 sm:space-y-2">
@ -327,11 +320,6 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
) : ( ) : (
/* Login Form */ /* Login Form */
<form className="space-y-4 sm:space-y-5 py-4 sm:py-6" onSubmit={handleLogin}> <form className="space-y-4 sm:space-y-5 py-4 sm:py-6" onSubmit={handleLogin}>
{error && (
<div className={`p-3 rounded-lg border ${isDark ? 'bg-red-900/20 border-red-800' : 'bg-red-50 border-red-200'}`}>
<p className={`text-sm ${isDark ? 'text-red-200' : 'text-red-800'}`}>{error}</p>
</div>
)}
{/* Email Field */} {/* Email Field */}
<div className="space-y-1.5 sm:space-y-2"> <div className="space-y-1.5 sm:space-y-2">

View File

@ -29,12 +29,15 @@
"react": "19.2.0", "react": "19.2.0",
"react-day-picker": "^9.11.1", "react-day-picker": "^9.11.1",
"react-dom": "19.2.0", "react-dom": "19.2.0",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"zod": "^4.1.12" "zod": "^4.1.12"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
"@types/hast": "^3.0.4",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",

File diff suppressed because it is too large Load Diff