Next.js Discord

Discord Forum

I am getting 2 errors when trying to log-in and I don't understand where they come from

Answered
Sun bear posted this in #help-forum
Open in Discord
Avatar
Sun bearOP
My Login Page is SSR and I am using Supabase for login in. I don't get any errors in the code but I get errors in the console and after I click sign-in the page stays the same and the only difference is that the input fields get emptied and I get those 2 errors in the console.
You can find the code for my log-in page attached bellow.
Image
Answered by Cimarrón Uruguayo
and import that action directly in the client component
View full answer

651 Replies

Avatar
Sun bearOP
// app/[lang]/login/page.tsx

import { MdEmail, MdKey } from "react-icons/md";
import Link from 'next/link';
import { cookies } from 'next/headers';
import { createClient } from '@/utils/supabase/server';
import { getDictionary } from '@/app/translation/dictionary';
import { Locale } from '@/i18n.config';
import { InputField } from '@/components/InputField';
import { Logo } from "@/components/Logo";

export default async function Login({
  searchParams,
  params: { lang }
}: {
  searchParams: { message: string },
  params: { lang: Locale }
}) {
  console.log('Login Page: Server-side rendering');
  const { page } = await getDictionary(lang);
  const cookieStore = cookies();
  const supabase = createClient(cookieStore);

  const signIn = async (event: React.FormEvent<HTMLFormElement>) => {
    'use server';
    event.preventDefault();


    const formData = new FormData(event.currentTarget);
    const email = formData.get('email') as string;
    const password = formData.get('password') as string;

    const { data: authData, error } = await supabase.auth.signInWithPassword({
        email,
        password,
    });

    const user = authData?.user;

   if (error || !user) {
    console.error('Login error:', error?.message);
    return `/login?message=${page.login.errorMessage}`;
}
const { data: userData, error: userDataError } = await supabase
      .from('Unconfirmed Users')
      .select('*')
      .eq('user_id', user.id)
      .single();

    if (userDataError) {
      console.error('Error fetching user data:', userDataError);
      return `/login?message=${page.login.errorMessage}`;
    }

    if (userData && userData.initialAccountSetup === 'False') {
      const targetTable = userData.accountType === 'Job Seeker' ? 'Job Seekers' : 'Recruiters';
      const { error: insertError } = await supabase
        .from(targetTable)
        .insert([userData]);

      if (insertError) {
        console.error('Error inserting user data:', insertError);
        return `/login?message=${page.login.errorMessage}`;
      }

      const { error: deleteError } = await supabase
        .from('Unconfirmed Users')
        .delete()
        .match({ user_id: user.id });

      if (deleteError) {
        console.error('Error deleting unconfirmed user:', deleteError);
      }

      const { error: updateError } = await supabase
        .from(targetTable)
        .update({ initialAccountSetup: 'Completed' })
        .match({ user_id: user.id });

      if (updateError) {
        console.error('Error updating initial account setup status:', updateError);
      }
    }

    return '/';
  };

return(
//...
 <form
            className="flex flex-col items-center justify-center gap-2 text-foreground"
            style={{ marginTop: '0px', width: '100%' }}
            onSubmit={signIn}
          >
//...
Avatar
Sun bearOP
Up!
Avatar
Toyger
it's some safari unique behaviour, try in chrome or firefox should be fine, if it's not break logic you can ignore that
Avatar
Sun bearOP
Holy * it shows my email and password in the url after I click login
Image
And 2 errors or warnings in the console
Image
Avatar
Toyger
this only warnings, about your password, if you'll add method="POST" to your form it should hide them in request because of POST
Avatar
Sun bearOP
Thank you very much, that solved it. However I noticed that there are 2 other errors that appear for a fraction of time before they disappear, right after I press sign in. It's those 2 errors.
Image
Avatar
Toyger
your server action have some problem in it, you need to check output logs in vercel to see are there some errors presented
Avatar
Sun bearOP
I see. Hmm, I'm not using Vercel though but thank you for your suggestion and help.
Avatar
gin
hey
i would recommend you to pu the server action in a seperate "use server" file
and then call it with useFormState
Avatar
Sun bearOP
Here is what my localhost server log says
Image
But my login page is already SSR
Avatar
gin
U can render the form on the client
its always better to do that
My opinion
with useFormState and useFormStatus you can get the pending state and the output
of the action
Avatar
Sun bearOP
I see, I can try and do that. And where would I put this server action file ?
Should I put it in my components folder ?
Avatar
gin
create a folder called actions and inside there
example: handleLoginSubmit
something like this
"use server";
export const yourAction = async (prevSate: any, formData: FormData) => {}
// inside action
return {
    message: "Your password is wrong!",
  };

// on the client:
const [state, formAction] = useFormState(yourAction, {
        message: ''
    });
Avatar
Sun bearOP
Why is this stuff so complicated
Avatar
gin
and then in useEffect check if state is true and get the message
"use client";
function YourButton() {
const { pending } = useFormStatus();

return (
  <button type="submit" loading={pending}></button> // or disabled, set disabled true when pending or do something else
)
}

function YourForm(){
const [state, formAction] = useFormState(yourAction, {
        message: ''
    });
return (
<form action={formAction}>
            <input name="username" value= />
            <input name="password" value= />
            <YourButton />
        </form>
)
}
hope this hepls @Sun bear
Avatar
Sun bearOP
It complicates things actually
Image
Looks like I need some additional hooks for that to work which I don't have and I have no idea how those hooks are supposed to look like for this to work like you said
The only hook file I have is this one
// hooks/useMounted.ts
import { useEffect, useState } from "react";

export default function useMounted(): boolean {
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
    return () => setMounted(false);
  }, []);

  return mounted;
}
Avatar
DirtyCajunRice | AppDir
@Sun bear thought i told you to use react-hook-form 😂
Avatar
Sun bearOP
I'm using that
Also hello there 😄
Oh oh oh, I see what you mean. I'm actually using that but only for my sign-up pages 😄
Avatar
DirtyCajunRice | AppDir
is all this in 1 file?
you are all jumbled up with server and client logic in 1
Avatar
Sun bearOP
Yes it's 1 single file.
Avatar
DirtyCajunRice | AppDir
do what abe said with the server actions, and just move the server logic to them
that way its safe
Avatar
DirtyCajunRice | AppDir
use shadcn/ui login page example
its a very pretty and mostly built example
that will get you 95% of the way there if you compare notes with it
Avatar
Sun bearOP
I'll update the page to use zod and react hook forms and check back with you guys here after it's doen to let me know if it's correct and then I'll split things up. I'll do this in small milestones so that I don't screw tings up more than they are.
Avatar
Cimarrón Uruguayo
huh?
Avatar
Sun bearOP
Okay, so here is the page now, please let me know if this is a correct implementation of Zod & React-Hook-Forms
Image
Avatar
Cimarrón Uruguayo
bro i can't read that
Avatar
Sun bearOP
And it doesn't work anymore, of course.
Image
Avatar
Cimarrón Uruguayo
yes because you're using cookies and a hook in the same file
former is for server components, latter is for client components
Avatar
Cimarrón Uruguayo
lmao
Avatar
Sun bearOP
lol 😂
Avatar
Cimarrón Uruguayo
ok I tried opening in separate browser and enlarging
too pixelated
can't say i didn't try
Avatar
Sun bearOP
Oh my bad I forgot pastebin exists
Avatar
DirtyCajunRice | AppDir
Avatar
Sun bearOP
Anyway, I will create a link with the code, give me a moment
Avatar
Cimarrón Uruguayo
i don't get what ur trying to say
Avatar
Sun bearOP
Should I do what I did with my sign-up page ?
// app/[lang]/signup/page.tsx

import { getDictionary } from '@/app/translation/dictionary';
import SignUp from '@/components/sign-up-page/sign-up-form';

// Define the type for props
type Props = {
  params: {
    lang: "en" | "ro";
  }
}

const Page = async ({ params: { lang } }: Props) => {
  const dictionary = await getDictionary(lang);
  
  return (
    <>
      <SignUp dictionary={dictionary} lang={lang} />
    </>
  );
}

export default Page;
This is the SSR part of the page. The other part of the page is a client page component stored under my components folder.
Somehow the code figures out the rest (don't ask me how)
Avatar
Cimarrón Uruguayo
mightt be better if DCR answers this
im inexperienced to this stuff
Avatar
Sun bearOP
I see but just so you know I appreciate very much that you're trying to help. I think you're awesome man, thank you very much.
Avatar
Cimarrón Uruguayo
yeah that works
move the hook part into CC
Avatar
DirtyCajunRice | AppDir
i havent been responding because im on mobile and i cant really read the code blocks well on discords shit mobile app
i will check it in the morning tho
Avatar
Sun bearOP
I've posted a link with the code by the way.
here
Avatar
DirtyCajunRice | AppDir
oh thats way better
ok sec
quick question
are supabase calls meant to be from the server or client
i would assume server right?
Avatar
Cimarrón Uruguayo
must be server
Avatar
DirtyCajunRice | AppDir
yeah so just take that 1 line in your handle submit
and move it to a server action
and call that server action in its place
to ensure its server side
Avatar
Sun bearOP
Hmm, no idea. I think so. They have specific documentation for how it works with the app router I think, here https://supabase.com/docs/guides/auth/auth-helpers/nextjs
But can't figure if it's from server or client, could be both lol.
Avatar
DirtyCajunRice | AppDir
oh
ohoh
so that call is already calling to an api route you had to make
nvm you dont need a server action
Avatar
Sun bearOP
lol
Avatar
DirtyCajunRice | AppDir
though arguably you could just move the route logic directly into the server action
but i digress
so wait. what is the actual error
Avatar
Cimarrón Uruguayo
supabase is being get'ed on the server for lack of better phrasing
this i think
Avatar
Sun bearOP
Currently it's this
Image
Avatar
DirtyCajunRice | AppDir
in literally every other context i would say “yeah theres already a word for that… got” but… not this one lol
oh
useForm has to happen in a client component
Avatar
Sun bearOP
So I suppose I have to split the page into 2 separate pages then like I did with my sign-up page ?
Avatar
DirtyCajunRice | AppDir
nono
just make a file next to the page file
like a … login-form.tsx
and put the form part in there
as a client component
so the page itself and outer stays server
Avatar
Cimarrón Uruguayo
basically server components and state don't go together
so any aspects of useXYZ should go in a client component
but u can still wrap the client component with a server component
Avatar
DirtyCajunRice | AppDir
exactly
nailed it
Avatar
Sun bearOP
Image
Avatar
Cimarrón Uruguayo
tough luck
Avatar
Sun bearOP
I've split the pages but I get another error now.

1. app/[lang]/login/page.tsx
https://paste.ec/paste/DUuTbfZy#lxKvnyGsnL31B8zz7ZbFhUy7Gye03OEYc6-W2eJi9e/

2. app/[lang]/login/login-form.tsx
https://paste.ec/paste/KKUohCKk#l0nNwPo5rmctvReA07ZE-3eI/FboDmt424v8zTMjk7Z
Image
UP
Avatar
DirtyCajunRice | AppDir
i dont know anything about the locale stuff
im just a dumb american
😄
Avatar
Sun bearOP
lol I think you're very smart
Avatar
DirtyCajunRice | AppDir
no you gotta play along :kek:
I think it has to do with my dictionary files but I don't understand
Image
Avatar
DirtyCajunRice | AppDir
seems more like you are checking for a nested key of an object that doesnt exist
Avatar
Sun bearOP
Can you please look in the code and let me know if that is the case ? GPT seems to have no idea why this is happening so it cannot help me with this error.
And as you can see I do have the translation key in my dictionary and in the json files too, I double checked.
Avatar
DirtyCajunRice | AppDir
you need to console.log your dictionary in your login form. i bet its empty or undefine
Avatar
Sun bearOP
Roger
Here are the results, it looks like there's actually 2 errors
Image
Avatar
DirtyCajunRice | AppDir
yep. so trace those down
the second one is obvious
you dont have a login key in your dictionary for that language
Avatar
Sun bearOP
I think I do.
Image
Image
Avatar
DirtyCajunRice | AppDir
that is not a login key.
do you see how that is the login object ALREADY
that means your dictionary isnt your dictionary
its a sub section of it
Avatar
Sun bearOP
Image
I did some loggin together with GPT and it seems to have arrived at a similar conclusion.
My question is why is it missing the Login key ? I don't want to use the translation like it said, I want to keep it like it is with the login key.
Because that''s how I do my translation on my other pages so I don't want one page to do it one way and the others to do it in another way.
As in, I want to keep using:

dictionary.login.signInButtonLabel

Not do dictionary.signInButtonLabel
Avatar
DirtyCajunRice | AppDir
you are passing the login sub object to it somehow
Avatar
Sun bearOP
Can you please have a look ? Both my files code is here. I am probably doing something wrong in one of these 2 files because the dictionary structure works just fine as it is with my other pages. If you have a bit of time.
Avatar
DirtyCajunRice | AppDir
1. line 128
Avatar
Sun bearOP
this line ? dictionary={page.login}
Avatar
DirtyCajunRice | AppDir
yep
Avatar
Sun bearOP
Do I need to delete the login key from there ?
Avatar
DirtyCajunRice | AppDir
exactly!
Avatar
Sun bearOP
I'm starting to learn programming with these many issues on my head lol 😁
Huh, page loads and both errors are gone 😮
How ?? The other error was complaining something about SSR it wasn't even related to this.
So there were 2 error, the one at the top was complaining about something elese
Image
Now that one is gone too. I don't understand
Image
Avatar
Sun bearOP
And now as soon as I try to log-in I get this error
Image
Avatar
Sun bearOP
Issue persists after upgrading to the latest version of next.js
Image
Avatar
DirtyCajunRice | AppDir
its telling you whats wrong
Avatar
Sun bearOP
The other Error came back!
Image
Avatar
Sun bearOP
You gotta be kidding me
Image
I've already split my login page in 2 for exactly THIS purpose to avoid these issues now it's telling me I have to split the code yet again.
Avatar
DirtyCajunRice | AppDir
pretty sure you dont need to split
just need to move onSignIn to your form
Avatar
Sun bearOP
Thank you for helping but if I do that it tells me I won't be able to perform Supabase operations anymore (and I need those operations to run upon sign in)
Image
Avatar
DirtyCajunRice | AppDir
you are trusting a bot man 😅
change the signIn function
from a const es6 definition
to a function es5
probably will fix it
Avatar
Sun bearOP
Okay so please bare with me as I have no idea what you just said or what I'm doing but I'm trying my best. I'll put GPT aside for a bit and try to do it without it.
So in my page.tsx there is only 1 mention of OnSignIn. Do I have to move this line ?
Image
Avatar
DirtyCajunRice | AppDir
paste just the signin function here
Avatar
Sun bearOP
const signIn = async (data: LoginFormData) => {
    'use server';
    const { email, password } = data;

    const { data: authData, error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    // Access user from the authData object
    const user = authData?.user;

    if (error || !user) {
      console.error('Login error:', error?.message);
      return `/login?message=${page.login.errorMessage}`;
    }

    // Check if user has completed initial account setup
    const { data: userData, error: userDataError } = await supabase
      .from('Unconfirmed Users')
      .select('*')
      .eq('user_id', user.id)
      .single();

    if (userDataError) {
      console.error('Error fetching user data:', userDataError);
      return `/login?message=${page.login.errorMessage}`;
    }

    if (userData && userData.initialAccountSetup === 'False') {
      // Move user data to the respective table based on accountType
      const targetTable = userData.accountType === 'Job Seeker' ? 'Job Seekers' : 'Recruiters';
      const { error: insertError } = await supabase
        .from(targetTable)
        .insert([userData]);

      if (insertError) {
        console.error('Error inserting user data:', insertError);
        return `/login?message=${page.login.errorMessage}`;
      }

      // Remove entry from Unconfirmed Users
      const { error: deleteError } = await supabase
        .from('Unconfirmed Users')
        .delete()
        .match({ user_id: user.id });

      if (deleteError) {
        console.error('Error deleting unconfirmed user:', deleteError);
      }

      const { error: updateError } = await supabase
        .from(targetTable)
        .update({ initialAccountSetup: 'Completed' })
        .match({ user_id: user.id });

      if (updateError) {
        console.error('Error updating initial account setup status:', updateError);
      }
    }

    return '/';
  };

I hope this is the right one.
Avatar
DirtyCajunRice | AppDir
change the first line from this
const signIn = async (data: LoginFormData) => {

to this
async function signIn(data: LoginFormData) {
error gone?
Avatar
Sun bearOP
Doesn't look like it. I should restart the server and retry.
Image
Yes, still there after restarting.
And loging in doesn't work either.
Avatar
DirtyCajunRice | AppDir
what is that prop
LoginFormData
server actions have to have exactly the prop its designed to have
change LoginFormData to FormData
Avatar
Sun bearOP
interface LoginFormProps {
  dictionary: any; // Define the proper type for your dictionary
  onSignIn: (data: LoginFormData) => void;
  errorMessage: string | null;
}

const LoginForm: React.FC<LoginFormProps> = ({ dictionary, onSignIn, errorMessage }) => {
  // Define Zod schema
  //console.log('Dictionary contents:', dictionary);
  const loginSchema = z.object({
    email: z.string().email({ message: dictionary.invalidEmailError }),
    password: z.string().min(6, { message: dictionary.passwordMinLengthError })
  });

I hope this is the right snippet to share.
Avatar
DirtyCajunRice | AppDir
oh. yeah man you cant pass that
a server action is strict
it passes 1 data type by default
your signIn Function
change the prop to data: FormData
does the error persist
Avatar
Sun bearOP
Hmm I don't see it anymore! Let me double check by restarting!
Image
Still there 😕
Avatar
DirtyCajunRice | AppDir
that doesnt tell me where the error is though
are you sure its that function
Avatar
Sun bearOP
So let me tell you when the error happens. So after restarting the server, when I acces localhost/login (my login page basically) that's when it appears
Avatar
DirtyCajunRice | AppDir
yes but it should be showing you where
in the ui
in the error overlay
Avatar
Sun bearOP
Error overlay ? Where is that ?
Image
Avatar
DirtyCajunRice | AppDir
browser
Avatar
Sun bearOP
I can't see it
Image
It only appears in the vscode terminal hmm let me try reloading the page
No it doesn't appear even if I keep the console opened, restart the server and reload the page 😕
Image
If there is a way to trace that error that appears in the terminal just let me know and I'll try to do it.
Avatar
DirtyCajunRice | AppDir
that makes no sense
when you click login
whats the actual error now
Avatar
Sun bearOP
In the terminal is this one
Image
In the browser is this one:
Unhandled Runtime Error
Error: Connection closed.
Call Stack
tu

/Users/johnnyrobert/Next JS/Projects/jobs-bringer-web/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (35:305904)
e.exports.<anonymous>

/Users/johnnyrobert/Next JS/Projects/jobs-bringer-web/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js (35:308051)
e.exports.emit

node:events (523:35)
finish

node:internal/streams/writable (757:10)
process.processTicksAndRejections

node:internal/process/task_queues (82:21)
And a bunch of red ones in the console
Image
Avatar
DirtyCajunRice | AppDir
wanna try something tricky? 😂
Avatar
Sun bearOP
Uhm, if it's unconventional no, I want things to be done properly since this will become a consumer product. But also I didn't move the SignIn function to my login-form.tsx page by the way. I know you said to move it.

I just renamed the things you said. Not sure if it's important anymore but I thought i should mention that.
Avatar
DirtyCajunRice | AppDir
wait
waaaait
your handleSubmit is weird
yeah
checked
thats whack
create your OWN on submit function
dont use that weird exported one
Avatar
Sun bearOP
Oh you mean not to use the Zod provide one ?
Avatar
DirtyCajunRice | AppDir
right
you need an async one. and you need to await your onLogin
i think thats why connection is closing
Avatar
Sun bearOP
Is this one looking good ?
 // Custom onSubmit function that awaits the onSignIn function
  const onSubmit = async (data: LoginFormData) => {
    try {
      await onSignIn(data);
    } catch (error) {
      console.error('Failed to sign in:', error);
    }
  };
Avatar
DirtyCajunRice | AppDir
yeah looks great.
give the form that instead
Avatar
Sun bearOP
Hmm, okay I will do that but in the meantime I am getting this error on my other login page after this update we did.
Image
Image
Avatar
DirtyCajunRice | AppDir
nono dont make the function in the login page
make it in the form
you wrapped at the wrong place thats all
Avatar
Sun bearOP
Yes it's in the form, it's not in the login page.
Avatar
DirtyCajunRice | AppDir
oh its a type error
fix your type in the form
in props
change LoginFormData at the top to FormData
and change string to void
Avatar
Sun bearOP
Which string exactly ?
All of them ?
Or only this one ?
Image
Avatar
DirtyCajunRice | AppDir
the line above the arrow
actually
that looks fine
ohhhhh
its in the actual server action
at the end you are returning "/"
for no reason
remove that
Avatar
Sun bearOP
I removed that. We have 2 errors now by the way.
Image
Image
Image
Avatar
DirtyCajunRice | AppDir
ok. for this one its easy
await onLogin(new FormData(data));
or can do
await onLogin(data as FormData)
or
await onLogin(data as any);
Avatar
Sun bearOP
It creates 2 errors if we do that
Image
Avatar
DirtyCajunRice | AppDir
its onSignIn
im tired lol
Avatar
Sun bearOP
It worked with the third option! (after renaming to SignIn)
Now there's only this error left (for now lol)
Image
Avatar
DirtyCajunRice | AppDir
ummm
Avatar
Sun bearOP
And the server one (the one in the terminal) which I'd really like to take care of as well.
Avatar
DirtyCajunRice | AppDir
show me the sign in func again
and the login form props
Avatar
Sun bearOP
//app/[lang]/login/login-form.tsx
interface LoginFormData {
  email: string;
  password: string;
}

interface LoginFormProps {
  dictionary: any;
  onSignIn: (FormData: FormData) => Promise<void>;
  errorMessage: string | null;
}

const LoginForm: React.FC<LoginFormProps> = ({ dictionary, onSignIn, errorMessage }) => {
  
  const loginSchema = z.object({
    email: z.string().email({ message: dictionary.invalidEmailError }),
    password: z.string().min(6, { message: dictionary.passwordMinLengthError })
  });
//app/[lang]/login/page.tsx
// Now 'data' is typed with 'LoginFormData'
  async function signIn(data: LoginFormData) {
    'use server';
    const { email, password } = data;
const { data: authData, error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    const user = authData?.user;

    if (error || !user) {
      console.error('Login error:', error?.message);
      return `/login?message=${page.login.errorMessage}`;
    }

    const { data: userData, error: userDataError } = await supabase
      .from('Unconfirmed Users')
      .select('*')
      .eq('user_id', user.id)
      .single();

    if (userDataError) {
      console.error('Error fetching user data:', userDataError);
      return `/login?message=${page.login.errorMessage}`;
    }

    if (userData && userData.initialAccountSetup === 'False') {
   
      const targetTable = userData.accountType === 'Job Seeker' ? 'Job Seekers' : 'Recruiters';
      const { error: insertError } = await supabase
        .from(targetTable)
        .insert([userData]);

      if (insertError) {
        console.error('Error inserting user data:', insertError);
        return `/login?message=${page.login.errorMessage}`;
      }

      const { error: deleteError } = await supabase
        .from('Unconfirmed Users')
        .delete()
        .match({ user_id: user.id });

      if (deleteError) {
        console.error('Error deleting unconfirmed user:', deleteError);
       
      }

      const { error: updateError } = await supabase
        .from(targetTable)
        .update({ initialAccountSetup: 'Completed' })
        .match({ user_id: user.id });

      if (updateError) {
        console.error('Error updating initial account setup status:', updateError);
      }
    }
  };
Avatar
DirtyCajunRice | AppDir
this
should be data: FormData
not data: LoginFormData
Avatar
Sun bearOP
Now I get more errors 😔
Image
Avatar
DirtyCajunRice | AppDir
yeah thats fine
where you have = data
do = data as LoginFormData
errors gone?
Avatar
Sun bearOP
Image
Avatar
DirtyCajunRice | AppDir
data as unknown as LoginFormData
which basically means “shutup i know what im doing”
Avatar
Sun bearOP
Only the error before left now!
Image
Avatar
DirtyCajunRice | AppDir
every place you have a return in this func
instead call redirect with the string
Avatar
Sun bearOP
return /login?message=${page.login.errorMessage};

Can you please give me an example for one line ?
Avatar
DirtyCajunRice | AppDir
instead of this do

redirect(/login?message=${page.login.errorMessage});
Avatar
Sun bearOP
Oh lol.
Oh! Good thing I asked! 😅
Avatar
DirtyCajunRice | AppDir
stupid discord formatting
you know the backticks go there
Avatar
Cimarrón Uruguayo
redirect(`/login?message=${page.login.errorMessage}`);
Avatar
DirtyCajunRice | AppDir
exactly
Avatar
Cimarrón Uruguayo
`
Avatar
DirtyCajunRice | AppDir
and at the bottom of the func
where you were calling return /
call redirect('/')
Avatar
Sun bearOP
Hmm, after I updated one of the lines now I get more errors.
Image
Avatar
DirtyCajunRice | AppDir
well… 1. import redirect
2. return after
typescript doesnt understand it will stop the function
Avatar
Sun bearOP
Image
And I did import the redirect
Avatar
DirtyCajunRice | AppDir
did you tho
show me
Avatar
Cimarrón Uruguayo
from next/navigation?
did u import
Avatar
Sun bearOP
Image
Avatar
DirtyCajunRice | AppDir
hahaha.
Avatar
Cimarrón Uruguayo
nope
Avatar
Sun bearOP
From next navigation, got it! 😄 And sorry. I had no idea.
Avatar
DirtyCajunRice | AppDir
import { redirect } from 'next/navigation'
Avatar
Cimarrón Uruguayo
import { redirect } from 'next/navigation'
Avatar
DirtyCajunRice | AppDir
sokay
Avatar
Sun bearOP
Error gone for that line
Avatar
DirtyCajunRice | AppDir
technically you did import a redirect
Avatar
Cimarrón Uruguayo
what errors now
Avatar
DirtyCajunRice | AppDir
just not the redirect
he just needs to finish replacing the returns
Avatar
Cimarrón Uruguayo
mm ok
ngl this seems like a tough ask for someone unfamiliar with next to make a product for a client
Avatar
DirtyCajunRice | AppDir
its for him not for a client
Avatar
Cimarrón Uruguayo
oh wait what
no isn't he building something for a client
Avatar
Sun bearOP
//app/[lang]/login/page.tsx
// Define the shape of your form data
  async function signIn(data: FormData) {
    'use server';
    const { email, password } = data as unknown as LoginFormData;

    const { data: authData, error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    const user = authData?.user;

    if (error || !user) {
      console.error('Login error:', error?.message);
      redirect(`/login?message=${page.login.errorMessage}`);
      return;
    }

    const { data: userData, error: userDataError } = await supabase
      .from('Unconfirmed Users')
      .select('*')
      .eq('user_id', user.id)
      .single();

    if (userDataError) {
      console.error('Error fetching user data:', userDataError);
      redirect(`/login?message=${page.login.errorMessage}`);
      return;
    }

    if (userData && userData.initialAccountSetup === 'False') {
      const targetTable = userData.accountType === 'Job Seeker' ? 'Job Seekers' : 'Recruiters';
      const { error: insertError } = await supabase
        .from(targetTable)
        .insert([userData]);

      if (insertError) {
        console.error('Error inserting user data:', insertError);
        redirect(`/login?message=${page.login.errorMessage}`);
        return;
      }

      const { error: deleteError } = await supabase
        .from('Unconfirmed Users')
        .delete()
        .match({ user_id: user.id });

      if (deleteError) {
        console.error('Error deleting unconfirmed user:', deleteError);
      }

      const { error: updateError } = await supabase
        .from(targetTable)
        .update({ initialAccountSetup: 'Completed' })
        .match({ user_id: user.id });

      if (updateError) {
        console.error('Error updating initial account setup status:', updateError);
      }
    }
    redirect('/')
  };
Please let me know if this looks correct now, the errors are gone.
And by the way the returns do nothing it seems
Image
Avatar
DirtyCajunRice | AppDir
yet it was erroring
i dont trust vscode
¯_(ツ)_/¯
Avatar
Sun bearOP
Oh, gotcha!!
Avatar
DirtyCajunRice | AppDir
looks fine
Avatar
Cimarrón Uruguayo
Image
the type signature has return type never so that makes sense
Avatar
DirtyCajunRice | AppDir
um… what
Avatar
Cimarrón Uruguayo
you could write return redirect(...) to make more sense
or naye?
Avatar
DirtyCajunRice | AppDir
makes the same sense. may feel cleaner to some though
nothing wrong with it
Avatar
Sun bearOP
It's for my own business 😄 By the way if it works out I will try and actually pay the people that helped me, seriously but it will probably take some time after launch to see the results. I don't forget people that helped me.
Avatar
Cimarrón Uruguayo
ahh
yeah i prefer it
Avatar
Sun bearOP
Ok so should I do that ?
Avatar
DirtyCajunRice | AppDir
i just write my if statements in such a way that there are no further paths
so its moot
dont worry about that now. can do that when im asleep
does it still connection close
Avatar
Cimarrón Uruguayo
just aesthetics don't worry
Avatar
Sun bearOP
Ohhh Dirty Dirty I think we caught the error!
In the browser, finally!
Avatar
DirtyCajunRice | AppDir
😄
Avatar
Sun bearOP
Remember that weird terminal error ?
That appeared when just opening the login page
?
Avatar
DirtyCajunRice | AppDir
yeah
show me
Avatar
Sun bearOP
Well, now it appears when I click log in! In the browser!
Image
Avatar
DirtyCajunRice | AppDir
still doesnt tell me where that shithead is tho
just… out of morbid curiosity
Avatar
Sun bearOP
Image
Avatar
DirtyCajunRice | AppDir
if you comment out everything in log—-
ok show that line
Avatar
Sun bearOP
Image
Avatar
DirtyCajunRice | AppDir
um… what
onSubmit is passed tothe form directly is it not…?
Avatar
Cimarrón Uruguayo
how r u calling login form
Avatar
Sun bearOP
I will re-share the updated code with you since it may make it easier to check.
Avatar
DirtyCajunRice | AppDir
oh
ffs
line 51
remove handleSubmit
just pass onSubmit directly
stupid handleSubmit
Avatar
Cimarrón Uruguayo
also since ur using zod
// Define Zod schema
const loginSchema = z.object({
  email: z.string().email({ message: dictionary.invalidEmailError }),
  password: z.string().min(6, { message: dictionary.passwordMinLengthError })
});

type LoginFormData = z.infer<typeof loginSchema>
define ur schema and types outside the LoginForm component
Avatar
DirtyCajunRice | AppDir
yeah he is right
Avatar
Sun bearOP
Like this ?
Image
Avatar
Cimarrón Uruguayo
before
Avatar
DirtyCajunRice | AppDir
oh he cant
needs the locale dict
Avatar
Sun bearOP
Image
Avatar
Cimarrón Uruguayo
oh that's annoying
Avatar
DirtyCajunRice | AppDir
locale is annoying
everyone should just speak english
😂
dont get us in the weeds haha
Avatar
Cimarrón Uruguayo
😂
yeah that works
let zod resolve the types for you that's all
so u don't have to rewrite it urself (u did write the schema once)
Avatar
Sun bearOP
Hmm it gives some errors
Image
Avatar
Cimarrón Uruguayo
nonono
remove ()
sorry i shouldn't have spoken my mind earlier
Avatar
Sun bearOP
Now it gives more errors
Avatar
Cimarrón Uruguayo
type LoginFormData = z.infer<typeof loginSchema>
Avatar
Sun bearOP
Okay now I have bad news and good news guys.
Which one do you want first ?
Avatar
Cimarrón Uruguayo
bad
Avatar
require("anay-208")
bad
Avatar
Sun bearOP
The error is still there in the teminal, and it appears twice after loading the login page.
Image
And also, after the log-in, the login page just reloads and I checked my Supabase Database and the data from my table isn't moved either.
And the good news is there are no more errors in the browser after loging-in!
But I ain't sure if the login in actually works or not since it just reloads the login page after I click log-in.
And I can log-in again and again lol.
Avatar
DirtyCajunRice | AppDir
“feature complete: Ship it”
😂
Avatar
DirtyCajunRice | AppDir
you need to add lots of console.logs
so you can follow what happens when it happens
And about the annoying error in the terminal ?
It has to come from somewhere and I would like to fix it if it's possible.
Avatar
Sun bearOP
Hmm, hold on. So that error says:

"Error: Functions **__cannot__** be passed directly to **Client Components** unless you explicitly expose it by marking it with "use server". [function, ..., function]"

Now we know that when the error appeared in the browser it was on line 43, and it was complaining about this function:
  // Custom onSubmit function that awaits the onSignIn function
  const onSubmit = async (data: LoginFormData) => {
    try {
      await onSignIn(data as any);
    } catch (error) {
      console.error('Failed to sign in:', error);
    }
  };

Now, if we take what the error says word by word, and then look at the login form.tsx (the page in which the function above is in), we can see that the login form is indeed a client component!
// app/[lang]/login/login-form.tsx
'use client';

Could this be the reason why ?
Maybe it's complaining that that function is in a client component and it wants it in a server component!
Avatar
DirtyCajunRice | AppDir
nope
Avatar
Sun bearOP
Also, I just noticed this too for the function we've created.
Image
Avatar
DirtyCajunRice | AppDir
rofl
thats probably the actual error
what are you passing to form
I have no idea, hold on I will upload the updated code.
Avatar
DirtyCajunRice | AppDir
just show the 1 line lol
Avatar
Sun bearOP
interface LoginFormProps {
  dictionary: any; // Define the proper type for your dictionary
  onSignIn: (FormData: FormData) => Promise<void>; // Make sure this is typed to return a Promise
  errorMessage: string | null;
}

Is it this one ?
Avatar
DirtyCajunRice | AppDir
no
line 51
Avatar
Sun bearOP
Oh in the return!
Avatar
DirtyCajunRice | AppDir
you didnt just remove handleSubmit
your removed the entire damn onSubmit prop
😂
Hmm I just removed this line when you told me.
Image
I was supposed to only remove the handleSubmit string isn't it ? 😅
How is the line supposed to look ? Having it like this doesn't work.
onSubmit={onSubmit}
Avatar
DirtyCajunRice | AppDir
thats how it should be
define “doesnt work”
Avatar
Sun bearOP
Image
Avatar
Cimarrón Uruguayo
hmm no i think having the handleSubmit was right
Avatar
Sun bearOP
If I do it like that yes the error goes away.
But the error in the terminal is still there
Image
Avatar
DirtyCajunRice | AppDir
its not
put it back
Avatar
Cimarrón Uruguayo
is it not using react hook form?
Avatar
DirtyCajunRice | AppDir
remove the props from your function
Avatar
Cimarrón Uruguayo
omg
sorry my brain
Avatar
Sun bearOP
I will, just wanted to say that after adding handle submit back the error from the terminal also re-appears in browser, this time at line 46
Image
when trying to log-in
/ Custom onSubmit function that awaits the onSignIn function
  const onSubmit = async (data: LoginFormData) => {
    try {
      await onSignIn(data as any);
    } catch (error) {
      console.error('Failed to sign in:', error);
    }
  };

All of them ?
Avatar
DirtyCajunRice | AppDir
actually. i just checked my shit
im literally using it with async
with no issues
johnny i swear to god this is probably some supabase bullshit
Avatar
Sun bearOP
For me it says this
Image
Let me look if I can find the sign-in with password documentation for Supabase
I think that's what we're using and looking at the documentation they use async too!
Avatar
DirtyCajunRice | AppDir
no i am saying you can keep the handle submit wrapping it
im saying the console error is probably some supabase bullshit
Avatar
Sun bearOP
Image
Image
Wait so I need to add the handle submit back now ?
Avatar
Cimarrón Uruguayo
yes
it should be the intended usage of react-hook-form
Avatar
Sun bearOP
Hmm, well okay but now we're back to the terminal erorr. Well, at least we know where it's coming from now.
Image
Avatar
Cimarrón Uruguayo
Image
see example from https://react-hook-form.com/docs/useform documentation
Avatar
Sun bearOP
Image
Well we are using the supabase server in the login page.tsx
Image
Avatar
Cimarrón Uruguayo
ok
Avatar
Sun bearOP
And I do have a server file, I will share it here.
//utils/supabase/server.ts
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'

export const createClient = (cookieStore: ReturnType<typeof cookies>) => {
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options })
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: '', ...options })
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  )
}
Avatar
Cimarrón Uruguayo
make a signin-action.tsx file
I think passing server action to client components as props is why its complaining
so move it into this file
Avatar
Cimarrón Uruguayo
and import that action directly in the client component
Answer
Avatar
Sun bearOP
Hmm it sounds like it makes sense.
And what do I put in this signing action file ?
Avatar
Cimarrón Uruguayo
the function with the 'use server' directive
 async function signIn(data: FormData) {
    'use server';
this one
Avatar
Sun bearOP
Like this right ?
Image
Avatar
Cimarrón Uruguayo
yeah the entire function from ur page.tsx
not just that
Image
my eyes are a ibt tired
don't know where the signIn function ends
bbut yeah
all of it
Avatar
Sun bearOP
Phew, okay I think it's done, would you please double check if it's good ?

app/[lang]/login/page.tsx (Updated):
https://paste.ec/paste/zLiNjbZK#iXLWLEfvddm5U0bem-9CRpQMCilmOI0dLjmg2zYve9j

app/[lang]/login/signin-action.tsx (Updated):
https://paste.ec/paste/hCLgtSYH#MXXEgsCNFUDcgrE-AuCDYnbHmxixxvUTCo0fXAi7YFk
Avatar
Cimarrón Uruguayo
place the server action in the client component
page.tsx contains a server component
form component
Avatar
Sun bearOP
Oh my God I have to modify all 3 pages ?
Avatar
Cimarrón Uruguayo
so
here's how it originally was
you have the server component (page.tsx) that had the server action function (signIn) directly inside its exported component Login
you following me so far?
Avatar
Sun bearOP
Loud and clear so far!
Avatar
Cimarrón Uruguayo
yep
Avatar
Sun bearOP
And now we moved it into the server action file right ?
Avatar
Cimarrón Uruguayo
so that server action was passed as a prop to the child component (aka the client component) login-form.tsx
Avatar
Sun bearOP
I think it still is because I didn't change anything in that page yet.
Avatar
Cimarrón Uruguayo
so now what we're doing is, moving the server action function into its own file + importing it in the login-form directly
Avatar
Sun bearOP
But didn't we just do that already ? Cause we moved the entire function as you told me to that new file.
Avatar
Cimarrón Uruguayo
yep im just explaining you what we did
Avatar
Sun bearOP
Oh, understood.
Avatar
Cimarrón Uruguayo
yeah
call the server action in ur client component
Avatar
Sun bearOP
app/[lang]/login/login-form.tsx (Updated): https://paste.ec/paste/2ZOLCnW9#7GLgjUFGDR8Mf2qERrO3CqlURV52VXpvqWTlRgHOHU7

So what do I have to modify here or on which line to call ?
Avatar
Cimarrón Uruguayo
noticed 1 thing
Image
what is page being used for in the form
because atm ctrl f shows that its just being passed as parameter without usage in form
Avatar
Sun bearOP
I had this error after moving the code from the page.tsx to the server action file:
[{
    "resource": "/Users/johnnyrobert/Next JS/Projects/jobs-bringer-web/app/[lang]/login/page.tsx",
    "owner": "typescript",
    "code": "2554",
    "severity": 8,
    "message": "Expected 1 arguments, but got 2.",
    "source": "ts",
    "startLineNumber": 60,
    "startColumn": 54,
    "endLineNumber": 60,
    "endColumn": 58
}]

So I told Chat GPT about the error and it told me this:
Avatar
Cimarrón Uruguayo
oops
wrong window?
Avatar
Sun bearOP
Image
Image
Image
So that's why it looks like that. I hope this explains it.
Oh! I forgot to actually update the signup form page with that line!
But if I do that I get an error:
"message": "Expected 2 arguments, but got 1.",
Image
Avatar
Cimarrón Uruguayo
since we moved the server action outside of page.tsx, you'll need to remove onSignIn as a prop of LoginForm
onSignIn is no longer required because we can directly import the server action
this is because you don't have the page parameter passed to onSignIn
we moved the server action out, so no longer need to pass as prop to obtain it in the login-form
we obtain instead by importing from signin-action
does that make sense?
Avatar
Sun bearOP
So what do I have to do now ?

I currently have it like this:
onSignIn: (data: FormData, page: any) => Promise<void>;  // Make sure this is typed to return a Promise
Avatar
Cimarrón Uruguayo
yep the name of the interface is LoginFormProps
but we're no longer receiving that as a prop
Avatar
Sun bearOP
So I delete the "FormData" from there ?
Avatar
Cimarrón Uruguayo
u can remove this line
Avatar
Sun bearOP
const LoginForm: React.FC<LoginFormProps> = ({ dictionary, onSignIn, errorMessage }) => {

And here what do I need to do ?
Remove the onSignIn prop ?
Avatar
Cimarrón Uruguayo
yes
Avatar
Sun bearOP
And here do I remove this entire snippet ?
 // Custom onSubmit function that awaits the onSignIn function
  const onSubmit = async (data: LoginFormData) => {
    try {
      await onSignIn(data as any);
    } catch (error) {
      console.error('Failed to sign in:', error);
    }
  };
Avatar
Cimarrón Uruguayo
no keep that
Avatar
Sun bearOP
I was asking because it gives an error now
Image
Avatar
Cimarrón Uruguayo
remember this is the structure ur login-form will have
yes you need to just call the server action now
await signIn(formData, page)
signIn is imported from where?
Avatar
Sun bearOP
I am not importing it it's a custom function
I think
Oh, you mean the actual SignIn
It's from server actions I think
From the file we created where we moved it in there
Avatar
Cimarrón Uruguayo
yep
Avatar
Sun bearOP
That means I should probably import that file in this component as well right ?
Avatar
Cimarrón Uruguayo
just checking ur understanding
yes
exactly
Avatar
Sun bearOP
Now I get 2 errors
Image
And even if I write FormData I still get errors
Image
Avatar
Cimarrón Uruguayo
data?
Avatar
Sun bearOP
Like this ?
Image
Avatar
Cimarrón Uruguayo
1st param data
cos onSubmit has data as the param yes?
Avatar
Sun bearOP
Yup, and page!
Image
But it still gives errors 😦
Avatar
Cimarrón Uruguayo
what is this FormData type
Avatar
Sun bearOP
Hmm, I do not know. This is what it shows if I click on it.
Image
Avatar
Cimarrón Uruguayo
I think u want LoginFormData
also i have to go to sleep soon 🥶
Avatar
Sun bearOP
Image
Avatar
Cimarrón Uruguayo
putting SO just in case I suddenly go offline
ah sorry for not being clear
Avatar
Sun bearOP
Hmm. Do not worry it's okay I can wait until you guys are available.
Avatar
Cimarrón Uruguayo
Image
I intended the type of data
in ur server action file
to be of type LoginFormData
ok goodnight
sorry about leaving like this
Avatar
Sun bearOP
No worries!
Image
Just wanted to say that fixed that error, we only have error for the page one.
Avatar
Cimarrón Uruguayo
ok ill tell u why ur getting page error
its because u haven't passed page as a prop from page.tsx to login-form
add that and it should get resolved
anyways ill leave u to it or u can stop here
goodnight again!
Avatar
Sun bearOP
Thank you so much and goodnight. I'll see you guys later. I will wait in the mentime cause I don't want to mess up what we've done so far so I prefer fixing it together with you.
Avatar
Sun bearOP
Leaving this for when you guys are available next time.

app/[lang]/login/login-form.tsx (Updated):
https://paste.ec/paste/4Er4XgfB#N8oSpCu7n46rKVH2x3zA0SjH2z7v3UVqErA0u5Pen+B

app/[lang]/login/page.tsx (Updated):
https://paste.ec/paste/j4fOuBZr#+j8mLbdMdyWce5qLlEvDLkXTkteLTgehVFx5qPWL3YU

app/[lang]/login/signin-action.tsx (Updated):
https://paste.ec/paste/5UFEu84d#xeWnj3GCbksYZb8ZluTGxnJQEMs6-Bnpn1oS/BtJ055

And the error I am getting now when trying to access the login page.

Goodnight and thank you very much for all your help so far! 🙏🏻
Image
Avatar
Sun bearOP
UP
Avatar
Cimarrón Uruguayo
hello!
Avatar
Sun bearOP
Hey!
Avatar
Cimarrón Uruguayo
yeah the error tells you what's wrong
since the server action has a parent which is a client component
ur effectively saying, import a server component only feature into a client component
which will obviously make nextjs angry
Avatar
Sun bearOP
But isn't that why we've split the code in 3 files already ? I mean that's what's confusing me. We try avoiding one issue and after we apply the solution the issue is still there, then we split the page again and make a server action and the issue comes back again. If we go like this the login page will become a puzzle of small pages 😄
Avatar
Cimarrón Uruguayo
yeah so basically cookies can't be used in server component :(
@DirtyCajunRice | AppDir would appreciate some assistance here
pretty sure separating into server action file was the solution but unsure how to architect the fix from here
Image
ok im getting slightly confused by docs
Avatar
Sun bearOP
Dirty is probably on his yacht somewhere in the Caribbean. He is probably thinking how can he beat his own bottle opening record before sunset 😅
Avatar
DirtyCajunRice | AppDir
whatwhat
cookies absolutely can be used in a server component.
Avatar
Cimarrón Uruguayo
yeah they can as i just found out
Avatar
DirtyCajunRice | AppDir
which is exactly what the error says
Avatar
Cimarrón Uruguayo
meant client component
oopsies
Avatar
DirtyCajunRice | AppDir
Just working on this client's game ui in unreal
Image
Avatar
Sun bearOP
Wow, that's amazing. You are a game developer too ?
Avatar
DirtyCajunRice | AppDir
one of many things i can do, yes.
Avatar
joulev
hi please keep this thread mainly for the original question. chit-chat please go to #off-topic
Avatar
Sun bearOP
Avatar
Sun bearOP
If you guys think it's necessary I can split the files again I can try and do that, I don't mind it.
Avatar
Sun bearOP
The reason I am a bit bothered by it is that in the future if I need to make updates/add things, it becomes a bit more difficult to explain to Chat GPT how the code works for that page beforehand, and it increases the chances for it to make mistakes when it has to do modifications to the code across multiple pages so I always try to only split pages if necessary or if the code is too large for one page since GPT has issues with reading huge chunks of code in one go and remembering lots of information.
Avatar
Sun bearOP
And I just noticed this in page.tsx
Image
I suppose I just have to remove that
My code is here by the way if anyone wants to have a look and help me sort this issue out. In the meantime I'm trying to solve it with GPT.
Avatar
Sun bearOP
IT WORKED!
Avatar
Cimarrón Uruguayo
Sorry I was busy
what have I missed
Avatar
Sun bearOP
I will post the solution GPT gave me in a moment, I'd' really appreciate if you can have a look at it and just check things are secure and safe and optimised.
Avatar
Sun bearOP
And my signup code works, and the data is succesffully moved in the correct table upon sign-in, together with the correct account setup value!
Image
Avatar
Cimarrón Uruguayo
to check if its secure you'd probably just want to check that ur api isn't exposed on the client side
Avatar
Sun bearOP
How can I do that ?
And does the new code logic look solid to you ? What do you think about GPT's solution, do you think it's a good solution or is it a bad one ?
Avatar
Cimarrón Uruguayo
i can't really provide an opinion because I'm not "fluent" with server actions
the docs for nextjs say that you can directly attach an action with the action prop onto a form
Avatar
Sun bearOP
I appreciate the sincerity 👌
Avatar
Cimarrón Uruguayo
yeah I don't want to provide false informmation
If it works that's amazing persistance from your end :)
Avatar
Sun bearOP
@DirtyCajunRice | AppDir can you please have a look when you get some time ?
Avatar
joulev
hey. there's no way that a single question could entail 643 messages right? please open a new post if you want to ask a new question
Avatar
Cimarrón Uruguayo
Yeah I'm curious to see what he thinks
yeah these are resolved now
Image
Avatar
Sun bearOP
Joulev is right, I should open a review thread instead.
Avatar
joulev
mark answer and make a new threrad for other questions
Avatar
Sun bearOP
Yup, just let me think about which answer to mark as solution, I don't want to give the credit to GPT since most of the help was from abe and Dirty.
Avatar
joulev
good, thank you