<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Oliwier965</title>
    <description>The latest articles on DEV Community by Oliwier965 (@oliwier965).</description>
    <link>https://dev.to/oliwier965</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F522267%2F9957fd65-5c9a-44b6-9f04-51a37a85d5da.png</url>
      <title>DEV Community: Oliwier965</title>
      <link>https://dev.to/oliwier965</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/oliwier965"/>
    <language>en</language>
    <item>
      <title>NextJs image upload with zod validation</title>
      <dc:creator>Oliwier965</dc:creator>
      <pubDate>Sun, 14 Jul 2024 20:27:55 +0000</pubDate>
      <link>https://dev.to/oliwier965/nextjs-image-upload-with-zod-validation-38cf</link>
      <guid>https://dev.to/oliwier965/nextjs-image-upload-with-zod-validation-38cf</guid>
      <description>&lt;p&gt;Hello everyone 👋 Today I wanted to share one of my recent struggles: uploading files with validation in Next.js using useForm. There are many pitfalls for beginners, so I hope this tutorial will help 😊.&lt;/p&gt;

&lt;p&gt;Firstly, I will assume you have a basic Next.js setup ready and you know the basics. So, let’s get right into it.&lt;/p&gt;

&lt;p&gt;Install zod and zod-form-data. I'll be using npm for that. The command is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm i zod zod-form-data&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, let's create our page. I will also be using the shadcn library for this tutorial. If you want to use it too, run these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx shadcn-ui@latest init
npx shadcn-ui@latest add input
npx shadcn-ui@latest add button
npx shadcn-ui@latest add form
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;1) Create a file for your page with the .tsx extension. Add the "use client" directive at the top and import the necessary dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import { Button } from "@/components/ui/button";
import {
  Form,
  FormControl,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { userFormSchema } from "@/types/formSchema";
import { updateUser } from "@/lib/auth";

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Now, we will create our default export and create the necessary components inside.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default function Page() {
  const form = useForm&amp;lt;z.infer&amp;lt;typeof userFormSchema&amp;gt;&amp;gt;({
    resolver: zodResolver(userFormSchema),
    defaultValues: {
      login: "",
      email: "",
      password: "",
    },
  });

  function onSubmit(values: z.infer&amp;lt;typeof userFormSchema&amp;gt;) {
    const formData = new FormData();
    values.login &amp;amp;&amp;amp; formData.append("login", values.login);
    values.email &amp;amp;&amp;amp; formData.append("email", values.email);
    values.password &amp;amp;&amp;amp; formData.append("password", values.password);
    values.profileImage &amp;amp;&amp;amp; formData.append("profileImage", values.profileImage);

    updateUser(formData);
  }

  return (
    &amp;lt;Form {...form}&amp;gt;
      &amp;lt;form
        onSubmit={form.handleSubmit(onSubmit)}
        className="grid gap-4 lg:grid-cols-2 p-4 bg-black rounded-lg"
      &amp;gt;
        &amp;lt;FormField
          control={form.control}
          name="profileImage"
          render={({ field: { value, onChange, ...fieldProps } }) =&amp;gt; (
            &amp;lt;FormItem&amp;gt;
              &amp;lt;FormLabel&amp;gt;Profile picture&amp;lt;/FormLabel&amp;gt;
              &amp;lt;FormControl&amp;gt;
                &amp;lt;Input
                  className="bg-neutral-900"
                  type="file"
                  {...fieldProps}
                  accept="image/png, image/jpeg, image/jpg"
                  onChange={(event) =&amp;gt;
                    onChange(event.target.files &amp;amp;&amp;amp; event.target.files[0])
                  }
                /&amp;gt;
              &amp;lt;/FormControl&amp;gt;
              &amp;lt;FormMessage /&amp;gt;
            &amp;lt;/FormItem&amp;gt;
          )}
        /&amp;gt;
        &amp;lt;FormField
          control={form.control}
          name="login"
          render={({ field }) =&amp;gt; (
            &amp;lt;FormItem&amp;gt;
              &amp;lt;FormLabel&amp;gt;Login&amp;lt;/FormLabel&amp;gt;
              &amp;lt;FormControl&amp;gt;
                &amp;lt;Input
                  className="bg-neutral-900"
                  placeholder="Enter login"
                  {...field}
                /&amp;gt;
              &amp;lt;/FormControl&amp;gt;
              &amp;lt;FormMessage /&amp;gt;
            &amp;lt;/FormItem&amp;gt;
          )}
        /&amp;gt;
        &amp;lt;FormField
          control={form.control}
          name="email"
          render={({ field }) =&amp;gt; (
            &amp;lt;FormItem&amp;gt;
              &amp;lt;FormLabel&amp;gt;Email&amp;lt;/FormLabel&amp;gt;
              &amp;lt;FormControl&amp;gt;
                &amp;lt;Input
                  className="bg-neutral-900"
                  placeholder="Enter email"
                  {...field}
                /&amp;gt;
              &amp;lt;/FormControl&amp;gt;
              &amp;lt;FormMessage /&amp;gt;
            &amp;lt;/FormItem&amp;gt;
          )}
        /&amp;gt;
        &amp;lt;FormField
          control={form.control}
          name="password"
          render={({ field }) =&amp;gt; (
            &amp;lt;FormItem&amp;gt;
              &amp;lt;FormLabel&amp;gt;Password&amp;lt;/FormLabel&amp;gt;
              &amp;lt;FormControl&amp;gt;
                &amp;lt;Input
                  className="bg-neutral-900"
                  placeholder="Enter password"
                  {...field}
                /&amp;gt;
              &amp;lt;/FormControl&amp;gt;
              &amp;lt;FormMessage /&amp;gt;
            &amp;lt;/FormItem&amp;gt;
          )}
        /&amp;gt;
        &amp;lt;Button type="submit" className=" text-black w-full lg:col-span-2"&amp;gt;
          Submit
        &amp;lt;/Button&amp;gt;
      &amp;lt;/form&amp;gt;
    &amp;lt;/Form&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I want to explain some of this code. Look at the first FormField. We are destructuring from render because we need to parse the file.&lt;/p&gt;

&lt;p&gt;Now, look at the onSubmit function. We are creating formData and appending values because we can't parse non-plain objects to the server action, and the file is making it non-plain.&lt;/p&gt;

&lt;p&gt;Form is just a React useForm() that we are assigning userFormSchema to. We are using zodResolver as the resolver for client validation and setting default values.&lt;/p&gt;

&lt;p&gt;3) Now we will create userFormSchema. The name can be anything, but remember to use it correctly. You can create it in the same file or, like me, import it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { z } from "zod";
import { zfd } from "zod-form-data";

const userFormSchema = zfd.formData({
  login: z
      .string()
      .min(1, {
        message: "Login can't be empty.",
      })

  password: z
      .string()
      .min(8, {
        message: "Password must be mix 8 characters long.",
      })
      .max(20, {
        message: "Password must be max 20 characters long.",
      })
      .regex(
        /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/,
        "Password must include one small letter, one uppercase letter and one number"
      )

  email: z
      .string()
      .email({
        message: "Email is not in the correct format",
      })
      .min(1, {
        message: "Email can't be empty.",
      })

  profileImage: zfd
    .file()
    .refine((file) =&amp;gt; file.size &amp;lt; 5000000, {
      message: "File can't be bigger than 5MB.",
    })
    .refine(
      (file) =&amp;gt; ["image/jpeg", "image/png", "image/jpg"].includes(file.type),
      {
        message: "File format must be either jpg, jpeg lub png.",
      }
    )
});

export { userFormSchema };

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are using zfd from zod-form-data for the file field and setting our validation. We are using refine for file validation (size and format).&lt;/p&gt;

&lt;p&gt;4) Now, let's create our server action. The file must have the .ts extension and the 'use server' directive at the top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use server';

import { userFormSchema } from "@/types/formSchema";
import fs from "node:fs/promises";

const updateUser = async (data: FormData) =&amp;gt; {
  const safeData = userFormSchema.safeParse(data);
  if (!safeData.success) throw new Error("Invalid data");
  const { login, email, password, profileImage } = safeData.data;

  try {
    const arrayBuffer = await profileImage.arrayBuffer();
    const buffer = new Uint8Array(arrayBuffer);
    const filePath = `./public/uploads/${Date.now()}_${profileImage.name}`;

    await fs.writeFile(filePath, buffer);

    // Also here you cand do something with the rest of the data
  } catch (error: any) {
    throw new Error("Something went wrong");
  }
};

export { updateUser };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I haven't done anything with the rest of the data; this was only to show you how to combine both zod and zfd. We are using fs to write the file to our chosen location.&lt;/p&gt;

&lt;p&gt;That is all for this guide. I hope you enjoyed it 😊 and learned a lot 🧠. Have a nice day ☀️. Love you all 💖. If you have any feedback, feel free to leave it in the comments and please share this tutorial.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>nextjs</category>
      <category>zod</category>
    </item>
    <item>
      <title>Music Player</title>
      <dc:creator>Oliwier965</dc:creator>
      <pubDate>Sat, 06 Nov 2021 15:55:21 +0000</pubDate>
      <link>https://dev.to/oliwier965/music-player-2ggm</link>
      <guid>https://dev.to/oliwier965/music-player-2ggm</guid>
      <description>&lt;p&gt;Hi everyone I created a music player app and I want to share it with you, you can find it here:&lt;br&gt;
&lt;a href="https://oliwier965.github.io/Audio-Player/"&gt;https://oliwier965.github.io/Audio-Player/&lt;/a&gt; &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uxon5dUI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yaagst1ax9azq46ly4tf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uxon5dUI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yaagst1ax9azq46ly4tf.png" alt="Image description" width="611" height="901"&gt;&lt;/a&gt;&lt;br&gt;
Your feedback is very appreciated :)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Multiple buttons only one active on click</title>
      <dc:creator>Oliwier965</dc:creator>
      <pubDate>Sat, 31 Jul 2021 09:22:23 +0000</pubDate>
      <link>https://dev.to/oliwier965/multiple-buttons-only-one-active-on-click-50on</link>
      <guid>https://dev.to/oliwier965/multiple-buttons-only-one-active-on-click-50on</guid>
      <description>&lt;p&gt;Today I want to share with you how to do an effect, that if you have multiple buttons and you want to add active styling class on click but when you click on other button remove active class from last button and add it to the latest clicked. Interested so let's get into it. Here's how to accomplish it.&lt;/p&gt;

&lt;p&gt;1 Select all buttons. I named my variable btnsTip and selected them through data attribute, but it's up to you.&lt;br&gt;
&lt;code&gt;const btnsTip = document.querySelectorAll("[data-btn-tip]");&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;2 Add variable where we'll be storing last clicked button and set its value to null.&lt;br&gt;
&lt;code&gt;let activeBtn = null;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3 Loop through them and for each, create event listener on click and put event (e) in the parentheses to access it later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;btnsTip.forEach((btnTip) =&amp;gt; {
  btnTip.addEventListener("click", (e) =&amp;gt; {
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4 Do check if active button isn't equal to null because if it was, that will mean that there's no element stored because it's default variable value, and isn't equal to current target because we don't want to remove class on double click on current element. And we remove active class from our variable that mean that we remove active class from our last element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;btnsTip.forEach((btnTip) =&amp;gt; {
  btnTip.addEventListener("click", (e) =&amp;gt; {

    //New code here
    if ((activeBtn === null &amp;amp;&amp;amp; activeBtn !== e.currentTarget)) {
      activeBtn.classList.remove("active");
    }
    //

  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5 And last step we add active class to current element and set our activeBtn variable to current element that we have clicked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;btnsTip.forEach((btnTip) =&amp;gt; {
  btnTip.addEventListener("click", (e) =&amp;gt; {

    //New code here
    e.currentTarget.classList.add("active");
    //

    if ((activeBtn === null &amp;amp;&amp;amp; activeBtn !== e.currentTarget)) {
      activeBtn.classList.remove("active");
    }

    //New code here
    activeBtn = e.currentTarget;
    //

  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full Code Down Bellow&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const btnsTip = document.querySelectorAll("[data-btn-tip]");
let activeBtn = null;

btnsTip.forEach((btnTip) =&amp;gt; {
  btnTip.addEventListener("click", (e) =&amp;gt; {
    e.currentTarget.classList.add("active");

    if ((activeBtn === null &amp;amp;&amp;amp; activeBtn !== e.currentTarget)) {
      activeBtn.classList.remove("active");
    }

    activeBtn = e.currentTarget;
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you liked my post or was it useful to you, Like, Share, and Comment :)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
    </item>
  </channel>
</rss>
