Skip to content

Commit 6179e3f

Browse files
Jeremias Santosclaude
authored andcommitted
feat(pav-6): replace Facebook button with LinkedIn in login/register
- Add handleLinkedinLogin handler in RigthSide and RegisterSide - Replace Facebook button with LinkedIn button using inline SVG icon - Add tests for LinkedIn button click triggering OAuth redirect Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e8f7c6a commit 6179e3f

4 files changed

Lines changed: 77 additions & 6 deletions

File tree

frontend/src/components/login/RegisterSide.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import { register, getGoogleAuthUrl } from "@/services/authService";
2+
import { register, getGoogleAuthUrl, getLinkedinAuthUrl } from "@/services/authService";
33
import { Image } from "@unpic/react";
44
import { motion } from "framer-motion";
55
import { ArrowLeft, Eye, EyeOff } from "lucide-react";
@@ -75,6 +75,18 @@ export default function RegisterSide() {
7575
}
7676
};
7777

78+
const handleLinkedinLogin = async () => {
79+
setIsLoading(true);
80+
setApiError("");
81+
try {
82+
const url = await getLinkedinAuthUrl();
83+
window.location.href = url;
84+
} catch (err: any) {
85+
setApiError(err.message || "Erro ao iniciar login com LinkedIn.");
86+
setIsLoading(false);
87+
}
88+
};
89+
7890
const handleRevealPassword = () => setShowPassword((prev) => !prev);
7991

8092
const formatCpf = (value: string) => {
@@ -230,8 +242,10 @@ export default function RegisterSide() {
230242
<button type="button" onClick={handleGoogleLogin} disabled={isLoading} className="flex justify-center items-center py-3 px-4 border border-gray-200 dark:border-neutral-800 rounded-xl bg-white/50 dark:bg-neutral-800/50 hover:bg-gray-50 dark:hover:bg-neutral-700/50 transition-all shadow-sm cursor-pointer disabled:opacity-50">
231243
<Image src="/google.png" alt="Google" width={20} height={20} className="object-contain" />
232244
</button>
233-
<button type="button" disabled={isLoading} className="flex justify-center items-center py-3 px-4 border border-gray-200 dark:border-neutral-800 rounded-xl bg-white/50 dark:bg-neutral-800/50 hover:bg-gray-50 dark:hover:bg-neutral-700/50 transition-all shadow-sm cursor-pointer disabled:opacity-50">
234-
<Image src="/facebook.png" alt="Facebook" width={20} height={20} className="object-contain" />
245+
<button type="button" onClick={handleLinkedinLogin} disabled={isLoading} aria-label="LinkedIn" className="flex justify-center items-center py-3 px-4 border border-gray-200 dark:border-neutral-800 rounded-xl bg-white/50 dark:bg-neutral-800/50 hover:bg-gray-50 dark:hover:bg-neutral-700/50 transition-all shadow-sm cursor-pointer disabled:opacity-50">
246+
<svg className="h-5 w-5 fill-[#0A66C2]" viewBox="0 0 24 24">
247+
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
248+
</svg>
235249
</button>
236250
<button type="button" disabled={isLoading} className="flex justify-center items-center py-3 px-4 border border-gray-200 dark:border-neutral-800 rounded-xl bg-white/50 dark:bg-neutral-800/50 hover:bg-gray-50 dark:hover:bg-neutral-700/50 transition-all shadow-sm cursor-pointer disabled:opacity-50">
237251
<svg className="h-5 w-5 fill-gray-900 dark:fill-white transition-colors" viewBox="0 0 24 24">

frontend/src/components/login/RigthSide.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import { login, getGoogleAuthUrl } from "@/services/authService";
2+
import { login, getGoogleAuthUrl, getLinkedinAuthUrl } from "@/services/authService";
33
import { Image } from "@unpic/react";
44
import { motion } from "framer-motion";
55
import { ArrowLeft, Eye, EyeOff } from "lucide-react";
@@ -67,6 +67,18 @@ export default function RightSide() {
6767
}
6868
};
6969

70+
const handleLinkedinLogin = async () => {
71+
setIsLoading(true);
72+
setApiError("");
73+
try {
74+
const url = await getLinkedinAuthUrl();
75+
window.location.href = url;
76+
} catch (err: any) {
77+
setApiError(err.message || "Erro ao iniciar login com LinkedIn.");
78+
setIsLoading(false);
79+
}
80+
};
81+
7082
const handleRevealPassword = () => setShowPassword((prev) => !prev);
7183

7284
const handleSubmit = async (e: FormEvent) => {
@@ -233,8 +245,10 @@ export default function RightSide() {
233245
<button type="button" onClick={handleGoogleLogin} disabled={isLoading} className="flex justify-center items-center py-3 px-4 border border-gray-200 dark:border-neutral-800 rounded-xl bg-white/50 dark:bg-neutral-800/50 hover:bg-gray-50 dark:hover:bg-neutral-700/50 transition-all shadow-sm cursor-pointer disabled:opacity-50">
234246
<Image src="/google.png" alt="Google" width={20} height={20} className="object-contain" />
235247
</button>
236-
<button type="button" disabled={isLoading} className="flex justify-center items-center py-3 px-4 border border-gray-200 dark:border-neutral-800 rounded-xl bg-white/50 dark:bg-neutral-800/50 hover:bg-gray-50 dark:hover:bg-neutral-700/50 transition-all shadow-sm cursor-pointer disabled:opacity-50">
237-
<Image src="/facebook.png" alt="Facebook" width={20} height={20} className="object-contain" />
248+
<button type="button" onClick={handleLinkedinLogin} disabled={isLoading} aria-label="LinkedIn" className="flex justify-center items-center py-3 px-4 border border-gray-200 dark:border-neutral-800 rounded-xl bg-white/50 dark:bg-neutral-800/50 hover:bg-gray-50 dark:hover:bg-neutral-700/50 transition-all shadow-sm cursor-pointer disabled:opacity-50">
249+
<svg className="h-5 w-5 fill-[#0A66C2]" viewBox="0 0 24 24">
250+
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
251+
</svg>
238252
</button>
239253
<button type="button" disabled={isLoading} className="flex justify-center items-center py-3 px-4 border border-gray-200 dark:border-neutral-800 rounded-xl bg-white/50 dark:bg-neutral-800/50 hover:bg-gray-50 dark:hover:bg-neutral-700/50 transition-all shadow-sm cursor-pointer disabled:opacity-50">
240254
<svg className="h-5 w-5 fill-gray-900 dark:fill-white transition-colors" viewBox="0 0 24 24">

frontend/tests/unit/components/login/RegisterSide.test.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
33
import { beforeEach, describe, expect, it, vi } from "vitest";
44

55
const mockRegister = vi.fn();
6+
const mockGetLinkedinAuthUrl = vi.fn();
7+
68
vi.mock("@/services/authService", () => ({
79
register: (...args: any[]) => mockRegister(...args),
10+
getLinkedinAuthUrl: (...args: any[]) => mockGetLinkedinAuthUrl(...args),
811
}));
912

1013
vi.mock("@unpic/react", () => ({
@@ -37,6 +40,7 @@ describe("RegisterSide", () => {
3740

3841
beforeEach(() => {
3942
mockRegister.mockReset();
43+
mockGetLinkedinAuthUrl.mockReset();
4044
Object.defineProperty(window, "location", {
4145
configurable: true,
4246
value: { href: "" },
@@ -148,4 +152,22 @@ describe("RegisterSide", () => {
148152
expect(passwordInput).toBeDisabled();
149153
});
150154
});
155+
156+
it("redireciona para LinkedIn OAuth ao clicar no botao LinkedIn", async () => {
157+
mockGetLinkedinAuthUrl.mockResolvedValueOnce(
158+
"https://www.linkedin.com/oauth/v2/authorization?state=abc"
159+
);
160+
161+
render(<RegisterSide />);
162+
163+
const linkedinButton = screen.getByRole("button", { name: /linkedin/i });
164+
fireEvent.click(linkedinButton);
165+
166+
await waitFor(() => {
167+
expect(mockGetLinkedinAuthUrl).toHaveBeenCalled();
168+
expect(window.location.href).toBe(
169+
"https://www.linkedin.com/oauth/v2/authorization?state=abc"
170+
);
171+
});
172+
});
151173
});

frontend/tests/unit/components/login/RigthSide.test.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
44
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
55

66
const mockLogin = vi.fn();
7+
const mockGetLinkedinAuthUrl = vi.fn();
78

89
vi.mock("@/services/authService", () => ({
910
login: (...args: any[]) => mockLogin(...args),
11+
getLinkedinAuthUrl: (...args: any[]) => mockGetLinkedinAuthUrl(...args),
1012
}));
1113

1214
vi.mock("@unpic/react", () => ({
@@ -31,6 +33,7 @@ describe("RigthSide", () => {
3133

3234
beforeEach(() => {
3335
mockLogin.mockReset();
36+
mockGetLinkedinAuthUrl.mockReset();
3437
Object.defineProperty(window, "location", {
3538
configurable: true,
3639
value: { href: "" },
@@ -157,4 +160,22 @@ describe("RigthSide", () => {
157160
render(<RigthSide />);
158161
expect(screen.getByText(/esqueceu a senha/i)).toBeInTheDocument();
159162
});
163+
164+
it("redireciona para LinkedIn OAuth ao clicar no botao LinkedIn", async () => {
165+
mockGetLinkedinAuthUrl.mockResolvedValueOnce(
166+
"https://www.linkedin.com/oauth/v2/authorization?state=abc"
167+
);
168+
169+
render(<RigthSide />);
170+
171+
const linkedinButton = screen.getByRole("button", { name: /linkedin/i });
172+
fireEvent.click(linkedinButton);
173+
174+
await waitFor(() => {
175+
expect(mockGetLinkedinAuthUrl).toHaveBeenCalled();
176+
expect(window.location.href).toBe(
177+
"https://www.linkedin.com/oauth/v2/authorization?state=abc"
178+
);
179+
});
180+
});
160181
});

0 commit comments

Comments
 (0)