Skip to content
Open
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
94 changes: 94 additions & 0 deletions app/src/app/login/LoginClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"use client";

import Link from "next/link";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import Cookies from "js-cookie";
import { toast } from "sonner";
import MusicLoader from "@/components/MusicLoader";
import useAuthServices from "@/services/authService";
import { LoginEmailPayload } from "@/types";

export default function LoginClient() {
const router = useRouter();
const { useLoginEmail } = useAuthServices();
const loginMutation = useLoginEmail();

const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<LoginEmailPayload>();

const onSubmit = async (data: LoginEmailPayload) => {
try {
const result = await loginMutation.mutateAsync(data);
Cookies.set("audioblocks_jwt", result.token);
toast.success("Logged in successfully!");
router.push("/dashboard");
} catch (err) {
// onError on the mutation already toasts the message
}
};

const isBusy = isSubmitting || loginMutation.isPending;

return (
<div className="min-h-screen flex items-center justify-center bg-black px-4">
<div
className="w-full max-w-md p-8 space-y-6"
style={{ borderRadius: "16px", background: "#161616", border: "1px solid #2A2A2A" }}
>
<div>
<h1 className="text-white text-2xl font-bold">Log in</h1>
<p className="text-sm text-[#A3A3A3] mt-1">Welcome back to AudioBlocks.</p>
</div>

<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="flex flex-col">
<label className="text-sm font-medium text-white mb-2">Email</label>
<input
type="email"
{...register("email", { required: "Email is required" })}
placeholder="you@example.com"
className="text-white placeholder:text-[#6F6F6F] focus:outline-none px-4 h-12 rounded-2xl"
style={{ background: "#FFFFFF0A", border: "none" }}
/>
{errors.email && (
<span className="text-xs text-red-500 mt-1">{errors.email.message}</span>
)}
</div>

<div className="flex flex-col">
<label className="text-sm font-medium text-white mb-2">Password</label>
<input
type="password"
{...register("password", { required: "Password is required" })}
placeholder="????????"
className="text-white placeholder:text-[#6F6F6F] focus:outline-none px-4 h-12 rounded-2xl"
style={{ background: "#FFFFFF0A", border: "none" }}
/>
{errors.password && (
<span className="text-xs text-red-500 mt-1">{errors.password.message}</span>
)}
</div>

<button
type="submit"
disabled={isBusy}
className={`${isBusy ? "opacity-70 cursor-not-allowed" : ""} w-full rounded-lg cursor-pointer bg-[#D2045B] hover:bg-[#B8043F] text-white font-semibold px-6 py-3 transition-colors`}
>
{isBusy ? <MusicLoader small /> : "Log in"}
</button>
</form>

<p className="text-sm text-[#A3A3A3] text-center">
Don&apos;t have an account?{" "}
<Link href="/signup" className="text-[#D2045B] hover:underline">
Sign up
</Link>
</p>
</div>
</div>
);
}
98 changes: 8 additions & 90 deletions app/src/app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,94 +1,12 @@
"use client";
import type { Metadata } from "next";
import LoginClient from "./LoginClient";

import Link from "next/link";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import Cookies from "js-cookie";
import { toast } from "sonner";
import MusicLoader from "@/components/MusicLoader";
import useAuthServices from "@/services/authService";
import { LoginEmailPayload } from "@/types";
export const metadata: Metadata = {
title: "Log in | AudioBlocks",
description:
"Log in to AudioBlocks to manage your music, earnings, and fan engagement from the artist dashboard.",
};

export default function LoginPage() {
const router = useRouter();
const { useLoginEmail } = useAuthServices();
const loginMutation = useLoginEmail();

const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<LoginEmailPayload>();

const onSubmit = async (data: LoginEmailPayload) => {
try {
const result = await loginMutation.mutateAsync(data);
Cookies.set("audioblocks_jwt", result.token);
toast.success("Logged in successfully!");
router.push("/dashboard");
} catch (err) {
// onError on the mutation already toasts the message
}
};

const isBusy = isSubmitting || loginMutation.isPending;

return (
<div className="min-h-screen flex items-center justify-center bg-black px-4">
<div
className="w-full max-w-md p-8 space-y-6"
style={{ borderRadius: "16px", background: "#161616", border: "1px solid #2A2A2A" }}
>
<div>
<h1 className="text-white text-2xl font-bold">Log in</h1>
<p className="text-sm text-[#A3A3A3] mt-1">Welcome back to AudioBlocks.</p>
</div>

<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="flex flex-col">
<label className="text-sm font-medium text-white mb-2">Email</label>
<input
type="email"
{...register("email", { required: "Email is required" })}
placeholder="you@example.com"
className="text-white placeholder:text-[#6F6F6F] focus:outline-none px-4 h-12 rounded-2xl"
style={{ background: "#FFFFFF0A", border: "none" }}
/>
{errors.email && (
<span className="text-xs text-red-500 mt-1">{errors.email.message}</span>
)}
</div>

<div className="flex flex-col">
<label className="text-sm font-medium text-white mb-2">Password</label>
<input
type="password"
{...register("password", { required: "Password is required" })}
placeholder="••••••••"
className="text-white placeholder:text-[#6F6F6F] focus:outline-none px-4 h-12 rounded-2xl"
style={{ background: "#FFFFFF0A", border: "none" }}
/>
{errors.password && (
<span className="text-xs text-red-500 mt-1">{errors.password.message}</span>
)}
</div>

<button
type="submit"
disabled={isBusy}
className={`${isBusy ? "opacity-70 cursor-not-allowed" : ""} w-full rounded-lg cursor-pointer bg-[#D2045B] hover:bg-[#B8043F] text-white font-semibold px-6 py-3 transition-colors`}
>
{isBusy ? <MusicLoader small /> : "Log in"}
</button>
</form>

<p className="text-sm text-[#A3A3A3] text-center">
Don&apos;t have an account?{" "}
<Link href="/signup" className="text-[#D2045B] hover:underline">
Sign up
</Link>
</p>
</div>
</div>
);
return <LoginClient />;
}
120 changes: 120 additions & 0 deletions app/src/app/signup/SignupClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"use client";

import Link from "next/link";
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import Cookies from "js-cookie";
import { toast } from "sonner";
import MusicLoader from "@/components/MusicLoader";
import useAuthServices from "@/services/authService";
import { RegisterEmailPayload } from "@/types";

type SignupFormValues = Omit<RegisterEmailPayload, "role">;

export default function SignupClient() {
const router = useRouter();
const { useRegisterEmail } = useAuthServices();
const registerMutation = useRegisterEmail();

const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<SignupFormValues>();

// This app is the artist dashboard, so every signup here is an artist account.
const onSubmit = async (data: SignupFormValues) => {
try {
const result = await registerMutation.mutateAsync({ ...data, role: "artist" });
Cookies.set("audioblocks_jwt", result.token);
toast.success("Account created successfully!");
router.push("/dashboard");
} catch (err) {
// onError on the mutation already toasts the message
}
};

const isBusy = isSubmitting || registerMutation.isPending;

return (
<div className="min-h-screen flex items-center justify-center bg-black px-4">
<div
className="w-full max-w-md p-8 space-y-6"
style={{ borderRadius: "16px", background: "#161616", border: "1px solid #2A2A2A" }}
>
<div>
<h1 className="text-white text-2xl font-bold">Create your artist account</h1>
<p className="text-sm text-[#A3A3A3] mt-1">Join AudioBlocks to upload and manage your music.</p>
</div>

<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="flex flex-col">
<label className="text-sm font-medium text-white mb-2">Display name</label>
<input
{...register("name")}
placeholder="Add Display name"
className="text-white placeholder:text-[#6F6F6F] focus:outline-none px-4 h-12 rounded-2xl"
style={{ background: "#FFFFFF0A", border: "none" }}
/>
</div>

<div className="flex flex-col">
<label className="text-sm font-medium text-white mb-2">Username</label>
<input
{...register("username")}
placeholder="Add a username"
className="text-white placeholder:text-[#6F6F6F] focus:outline-none px-4 h-12 rounded-2xl"
style={{ background: "#FFFFFF0A", border: "none" }}
/>
</div>

<div className="flex flex-col">
<label className="text-sm font-medium text-white mb-2">Email</label>
<input
type="email"
{...register("email", { required: "Email is required" })}
placeholder="you@example.com"
className="text-white placeholder:text-[#6F6F6F] focus:outline-none px-4 h-12 rounded-2xl"
style={{ background: "#FFFFFF0A", border: "none" }}
/>
{errors.email && (
<span className="text-xs text-red-500 mt-1">{errors.email.message}</span>
)}
</div>

<div className="flex flex-col">
<label className="text-sm font-medium text-white mb-2">Password</label>
<input
type="password"
{...register("password", {
required: "Password is required",
minLength: { value: 8, message: "Password must be at least 8 characters" },
})}
placeholder="At least 8 characters"
className="text-white placeholder:text-[#6F6F6F] focus:outline-none px-4 h-12 rounded-2xl"
style={{ background: "#FFFFFF0A", border: "none" }}
/>
{errors.password && (
<span className="text-xs text-red-500 mt-1">{errors.password.message}</span>
)}
</div>

<button
type="submit"
disabled={isBusy}
className={`${isBusy ? "opacity-70 cursor-not-allowed" : ""} w-full rounded-lg cursor-pointer bg-[#D2045B] hover:bg-[#B8043F] text-white font-semibold px-6 py-3 transition-colors`}
>
{isBusy ? <MusicLoader small /> : "Sign up"}
</button>
</form>

<p className="text-sm text-[#A3A3A3] text-center">
Already have an account?{" "}
<Link href="/login" className="text-[#D2045B] hover:underline">
Log in
</Link>
</p>
</div>
</div>
);
}
Loading