Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes.
4 changes: 4 additions & 0 deletions src/App.tsx → frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { TooltipProvider } from "./components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Index from "./pages/Index";
import SignUp from "./pages/Signup";
import SignIn from "./pages/Signin";
const queryClient = new QueryClient();

const App = () => (
Expand All @@ -14,6 +16,8 @@ const App = () => (
<BrowserRouter>
<Routes>
<Route path="/" element={<Index />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/signin" element={<SignIn />} />
</Routes>
</BrowserRouter>
</TooltipProvider>
Expand Down
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
30 changes: 30 additions & 0 deletions frontend/src/components/InputField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { forwardRef } from "react";
import { Input } from "./ui/input";
import { Label } from "./ui/label";

interface InputFieldProps extends React.InputHTMLAttributes<HTMLInputElement> {
label: string;
error?: string;
}

export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(
({ label, error, ...props }, ref) => {
return (
<div className="space-y-2">
<Label htmlFor={props.id} className="text-sm font-medium">
{label}
</Label>
<Input
ref={ref}
{...props}
className={error ? "border-destructive focus-visible:ring-destructive" : ""}
/>
{error && (
<p className="text-sm text-destructive font-medium">{error}</p>
)}
</div>
);
}
);

InputField.displayName = "InputField";
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const TechStack = () => {
{techCategories.map((category, index) => (
<Card
key={index}
className="p-6 bg-gradient-card border-border hover:shadow-card transition-all duration-300 hover:-translate-y-1 group animate-fade-in-up"
className="p-6 bg-gradient-card border-border hover:shadow-card transition-all duration-300 hover:bg-primary/10 hover:-translate-y-1 group animate-fade-in-up"
style={{ animationDelay: `${index * 0.1}s` }}
>
<div className="mb-4 inline-block p-3 bg-background rounded-lg group-hover:text-primary transition-all">
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
67 changes: 67 additions & 0 deletions frontend/src/lib/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
export interface SignUpData {
email: string;
password: string;
confirmPassword: string;
}

export interface SignInData {
email: string;
password: string;
}

export interface AuthResponse {
success: boolean;
message: string;
user?: {
email: string;
id: string;
};
}

// Mock registration function
export const mockRegister = async (data: SignUpData): Promise<AuthResponse> => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// Simulate validation
if (data.email === "test@example.com") {
reject({
success: false,
message: "Email already exists",
});
} else {
resolve({
success: true,
message: "Registration successful!",
user: {
email: data.email,
id: Math.random().toString(36).substring(7),
},
});
}
}, 1500);
});
};

// Mock login function
export const mockLogin = async (data: SignInData): Promise<AuthResponse> => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// Simulate authentication
if (data.email === "demo@peercall.com" && data.password === "password123") {
resolve({
success: true,
message: "Login successful!",
user: {
email: data.email,
id: "demo-user-123",
},
});
} else {
reject({
success: false,
message: "Invalid email or password",
});
}
}, 1500);
});
};
File renamed without changes.
File renamed without changes.
File renamed without changes.
123 changes: 123 additions & 0 deletions frontend/src/pages/Signin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { useState } from "react";
import { useForm } from "react-hook-form";
import { Link } from "react-router-dom";
import { Button } from "../components/ui/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../components/ui/card";
import { InputField } from "../components/InputField";
import { toast } from "../hooks/use-toast";
import { mockLogin, SignInData } from "../lib/api";
import { Loader2 } from "lucide-react";

const SignIn = () => {
const [isLoading, setIsLoading] = useState(false);
const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm<SignInData>();

const onSubmit = async (data: SignInData) => {
setIsLoading(true);
try {
const response = await mockLogin(data);
toast({
title: "Welcome back!",
description: response.message,
});
reset();
// Navigate to dashboard or home page here
} catch (error: any) {
toast({
title: "Error",
description: error.message || "Login failed. Please try again.",
variant: "destructive",
});
} finally {
setIsLoading(false);
}
};

return (
<div className="min-h-screen flex items-center justify-center bg-gradient-subtle px-4 py-8">
<Card className="w-full max-w-md shadow-card">
<CardHeader className="space-y-1 text-center">
<CardTitle className="text-3xl font-bold bg-gradient-primary bg-clip-text text-primary">
PeerCall
</CardTitle>
<CardDescription className="text-base">
Sign in to your account
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<InputField
label="Email"
id="email"
type="email"
placeholder="you@example.com"
{...register("email", {
required: "Email is required",
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: "Invalid email address",
},
})}
error={errors.email?.message}
/>

<InputField
label="Password"
id="password"
type="password"
placeholder="••••••••"
{...register("password", {
required: "Password is required",
minLength: {
value: 6,
message: "Password must be at least 6 characters",
},
})}
error={errors.password?.message}
/>

<Button
type="submit"
className="w-full"
size="lg"
disabled={isLoading}
>
{isLoading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Signing in...
</>
) : (
"Sign In"
)}
</Button>
</form>

<div className="mt-4 text-center">
<p className="text-sm text-muted-foreground">
Demo credentials: demo@peercall.com / password123
</p>
</div>
</CardContent>
<CardFooter className="flex flex-col space-y-2">
<div className="text-sm text-muted-foreground text-center">
Don't have an account?{" "}
<Link
to="/signup"
className="text-primary hover:underline font-medium transition-colors"
>
Sign Up
</Link>
</div>
</CardFooter>
</Card>
</div>
);
};

export default SignIn;
Loading
Loading