fix: credentials login error and all-day resize bar position
- Fix NextAuth CredentialsProvider + PrismaAdapter conflict by stubbing adapter session methods (JWT strategy doesn't use DB sessions) - Improve login error handling with redirect:false for real error messages - Move all-day section resize bar to top when allDayPosition is "below" - Fix resize drag direction for top-positioned all-day handle v1.51.2 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
eeabe2c1c5
commit
918d87cbbb
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "my-weekly-todo-list",
|
||||
"version": "1.51.1",
|
||||
"version": "1.51.2",
|
||||
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -11,16 +11,35 @@ function LoginContent() {
|
||||
const error = searchParams.get('error');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [credError, setCredError] = useState<string | null>(null);
|
||||
|
||||
const handleCredentialsSignIn = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
setCredError(null);
|
||||
const formData = new FormData(e.currentTarget);
|
||||
|
||||
await signIn('credentials', {
|
||||
email: formData.get('email'),
|
||||
password: formData.get('password'),
|
||||
callbackUrl,
|
||||
});
|
||||
try {
|
||||
const result = await signIn('credentials', {
|
||||
email: formData.get('email'),
|
||||
password: formData.get('password'),
|
||||
redirect: false,
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
if (result.error === 'CredentialsSignin') {
|
||||
setCredError('Invalid email or password');
|
||||
} else {
|
||||
setCredError(result.error);
|
||||
}
|
||||
setIsLoading(false);
|
||||
} else if (result?.ok) {
|
||||
window.location.href = callbackUrl;
|
||||
}
|
||||
} catch {
|
||||
setCredError('An unexpected error occurred');
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoogleSignIn = () => {
|
||||
@ -42,11 +61,15 @@ function LoginContent() {
|
||||
</p>
|
||||
|
||||
{/* Error Message */}
|
||||
{error && (
|
||||
{(error || credError) && (
|
||||
<div className="weekly-auth-error">
|
||||
{error === 'CredentialsSignin'
|
||||
{credError
|
||||
? credError
|
||||
: error === 'CredentialsSignin'
|
||||
? 'Invalid email or password'
|
||||
: 'An error occurred during sign in'}
|
||||
: error === 'email_not_verified'
|
||||
? 'Please verify your email before signing in'
|
||||
: `An error occurred during sign in (${error})`}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@ -1653,22 +1653,22 @@ export default function WeeklyView() {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const resizingRef = useRef<{ target: 'someday' | 'allday'; startY: number; startHeight: number } | null>(null);
|
||||
const resizingRef = useRef<{ target: 'someday' | 'allday'; startY: number; startHeight: number; handleOnTop: boolean } | null>(null);
|
||||
|
||||
const startResize = useCallback((e: React.MouseEvent | React.TouchEvent, target: 'someday' | 'allday') => {
|
||||
const startResize = useCallback((e: React.MouseEvent | React.TouchEvent, target: 'someday' | 'allday', handleOnTop = false) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
||||
const section = target === 'someday' ? somedaySectionRef.current : (document.querySelector('.all-day-events-section') as HTMLElement);
|
||||
if (!section) return;
|
||||
resizingRef.current = { target, startY: clientY, startHeight: section.getBoundingClientRect().height };
|
||||
resizingRef.current = { target, startY: clientY, startHeight: section.getBoundingClientRect().height, handleOnTop };
|
||||
|
||||
const onMove = (ev: MouseEvent | TouchEvent) => {
|
||||
if (!resizingRef.current) return;
|
||||
const y = 'touches' in ev ? ev.touches[0].clientY : ev.clientY;
|
||||
const rawDelta = y - resizingRef.current.startY;
|
||||
// Top handle: dragging up = increase height (invert delta); bottom handle: normal
|
||||
const delta = resizingRef.current.target === 'someday' ? -rawDelta : rawDelta;
|
||||
const delta = resizingRef.current.handleOnTop ? -rawDelta : rawDelta;
|
||||
const newHeight = Math.max(40, Math.min(600, resizingRef.current.startHeight + delta));
|
||||
if (resizingRef.current.target === 'someday') setSomedayHeight(newHeight);
|
||||
else setAllDayHeight(newHeight);
|
||||
@ -5411,8 +5411,20 @@ export default function WeeklyView() {
|
||||
);
|
||||
if (allDayEvents.length === 0) return null;
|
||||
|
||||
const handleOnTop = effectiveAllDayPosition === "below";
|
||||
const resizeHandle = isAllDayExpanded ? (
|
||||
<div
|
||||
className="resize-handle"
|
||||
onMouseDown={(e) => startResize(e, 'allday', handleOnTop)}
|
||||
onTouchStart={(e) => startResize(e, 'allday', handleOnTop)}
|
||||
>
|
||||
<div className="resize-handle-bar" />
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{effectiveAllDayPosition === "below" && resizeHandle}
|
||||
<section
|
||||
className={`all-day-events-section ${isAllDayExpanded ? "expanded" : "collapsed"}`}
|
||||
style={isAllDayExpanded && allDayHeight ? { height: `${allDayHeight}px`, overflowY: 'auto' } : undefined}
|
||||
@ -5532,16 +5544,7 @@ export default function WeeklyView() {
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
{/* Resize handle - outside section so it's always at the bottom border */}
|
||||
{isAllDayExpanded && (
|
||||
<div
|
||||
className="resize-handle"
|
||||
onMouseDown={(e) => startResize(e, 'allday')}
|
||||
onTouchStart={(e) => startResize(e, 'allday')}
|
||||
>
|
||||
<div className="resize-handle-bar" />
|
||||
</div>
|
||||
)}
|
||||
{effectiveAllDayPosition === "above" && resizeHandle}
|
||||
</>
|
||||
);
|
||||
})();
|
||||
@ -7315,8 +7318,8 @@ export default function WeeklyView() {
|
||||
{somedayExpanded && (
|
||||
<div
|
||||
className="resize-handle"
|
||||
onMouseDown={(e) => startResize(e, 'someday')}
|
||||
onTouchStart={(e) => startResize(e, 'someday')}
|
||||
onMouseDown={(e) => startResize(e, 'someday', true)}
|
||||
onTouchStart={(e) => startResize(e, 'someday', true)}
|
||||
>
|
||||
<div className="resize-handle-bar" />
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { NextAuthOptions } from "next-auth";
|
||||
import type { Adapter } from "next-auth/adapters";
|
||||
import GoogleProvider from "next-auth/providers/google";
|
||||
import AzureADProvider from "next-auth/providers/azure-ad";
|
||||
import AppleProvider from "next-auth/providers/apple";
|
||||
@ -7,8 +8,24 @@ import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import { compare } from "bcryptjs";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// Wrap PrismaAdapter to prevent session creation for credentials provider
|
||||
// (known NextAuth v4 issue: adapter tries to create DB session even with JWT strategy)
|
||||
const prismaAdapter = PrismaAdapter(prisma) as Adapter;
|
||||
const adapter: Adapter = {
|
||||
...prismaAdapter,
|
||||
createUser: prismaAdapter.createUser,
|
||||
getUser: prismaAdapter.getUser,
|
||||
getUserByEmail: prismaAdapter.getUserByEmail,
|
||||
getUserByAccount: prismaAdapter.getUserByAccount,
|
||||
linkAccount: prismaAdapter.linkAccount,
|
||||
createSession: () => { return null as any; },
|
||||
getSessionAndUser: () => { return null as any; },
|
||||
updateSession: () => { return null as any; },
|
||||
deleteSession: () => { return null as any; },
|
||||
};
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
adapter: PrismaAdapter(prisma),
|
||||
adapter,
|
||||
providers: [
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID || "",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user