-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: feedback tutorial * fix: desc * fix: content fix * fix: content & sections
- Loading branch information
Showing
9 changed files
with
375 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
371 changes: 371 additions & 0 deletions
371
src/content/docs/sections/Frontend/Poradniki/feedback-form.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,371 @@ | ||
--- | ||
title: Przyjemny formularz feedback | ||
sidebar: | ||
order: 7 | ||
description: Jak utworzyć formularz feedbacku, który będzie przyjemny dla użytkownika i szybki w wykonaniu dla ciebie bez konieczności tworzenia panelu administracyjnego u siebie. | ||
--- | ||
|
||
import { Steps } from "@astrojs/starlight/components"; | ||
import ImageFeedback1 from "#assets/tutorials/feedback-1.png"; | ||
import ImageFeedback2 from "#assets/tutorials/feedback-2.png"; | ||
import ImageFeedback3 from "#assets/tutorials/feedback-3.png"; | ||
import ImageFeedback4 from "#assets/tutorials/feedback-4.png"; | ||
import ImageFeedback5 from "#assets/tutorials/feedback-5.png"; | ||
import ImageFeedback6 from "#assets/tutorials/feedback-6.png"; | ||
import ImageFeedback7 from "#assets/tutorials/feedback-7.png"; | ||
|
||
Elo żelo! Czy zastanawiałeś się kiedyś, jak zrobić formularz feedbacku, który będzie przyjemny dla użytkownika, a jednocześnie szybki w wykonaniu dla ciebie, bez konieczności tworzenia panelu administracyjnego u siebie? W tym poradniku dowiesz się, jak to osiągnąć z wykorzystaniem Twojego ulubionego frameworku Next.js (lub dowolnego innego, nawet zwykłego HTML-a) oraz backendu w postaci Formularzy Google. | ||
|
||
Zamysł jest taki, aby stworzyć w pełni własny formularz na swojej stronie, a odpowiedzi przeglądać wygodnie na stronie Formularzy Google lub w Arkuszach Google. Dzięki temu zaoszczędzisz czas na tworzeniu infrastruktury, jednocześnie zachowując pełną kontrolę nad wyglądem i działaniem formularza. | ||
|
||
## Założenia | ||
|
||
Technologie użyte: | ||
|
||
1. NextJS jako przykład frameworku na frontcie | ||
2. Google Forms jako backend z odpowiedziami | ||
3. shadcn/ui jako biblioteka komponentów | ||
|
||
## tl;dr | ||
|
||
Wszystko sprowadza się do wysłania formularza POST do Google Forms z odpowiednimi danymi: | ||
|
||
```html | ||
<form action="https://docs.google.com/forms/.../formResponse" method="POST"> | ||
<input type="text" name="entry.123456789" /> | ||
<input type="text" name="entry.987654321" /> | ||
<button type="submit">Submit</button> | ||
</form> | ||
``` | ||
|
||
Aczkolwiek zrobimy to w bardziej elegancki sposób z użyciem `react-hook-form` i `shadcn/ui` oraz zabezpieczeniem, przeciwko spamowaniu. | ||
|
||
## Let's gooo 🚀 | ||
|
||
To są szybkie kroki, dzięki którym stworzysz formularz feedbacku: | ||
|
||
<Steps> | ||
|
||
1. **Stwórz nowy formularz:** | ||
|
||
Przejdź na stronę [Google Forms](https://docs.google.com/forms/u/0/), stwórz nowy formularz i dodaj swoje pytania. | ||
|
||
<img src={ImageFeedback1} width={"100%"} height={"auto"} /> | ||
|
||
2. **Opublikuj formularz i stwórz arkusz** | ||
|
||
Następnie opublikuj publicznie swój forms: | ||
|
||
- Przycisk "Opublikuj" | ||
- W kategorii respondenci kliknij "Zarządzaj" | ||
- 'Dostęp ogólny -> Widok respondenta' zmień z "Politechnika Wrocławska" na "Każda osoba mająca link" | ||
- "Gotowe" -> "Opublikuj" | ||
|
||
<img src={ImageFeedback2} width={"100%"} height={"auto"} /> | ||
|
||
3. **Stwórz połączony arkusz google** | ||
|
||
Możesz przeglądać również odpowiedzi i łatwo je udostępniać dzięki arkuszom Google. Wystarczy, że po przejściu do zakładki odpowiedzi, <br/>klikniesz zielony przycisk "Link do Arkuszy" -> "Utwórz": | ||
|
||
<img src={ImageFeedback3} width={"100%"} height={"auto"} /> | ||
|
||
:::tip | ||
Arkusz ten będzie automatycznie aktualizowany o nowe odpowiedzi oraz zmiany w formularzu, więc nie musisz się o to martwić. Możesz go wygodnie udostępniać innym osobom. | ||
::: | ||
|
||
4. **Nowy komponent na stronie** | ||
|
||
Naszedł czas na dodanie formularza do swojej strony. Przejdźmy do Twojego projektu NextJS, stwórzmy nowy client komponent `FeedbackForm.tsx`: | ||
|
||
```tsx | ||
// FeedbackForm.tsx | ||
"use client"; | ||
|
||
import React from "react"; | ||
|
||
export function FeedbackForm() { | ||
return <div>Witam</div>; | ||
} | ||
``` | ||
|
||
5. **Wymagane dependencies** | ||
|
||
Do obsługi `<form>` użyjmy `react-hook-form` i komponentu shadcn/ui `Form`. | ||
Najpierw dodaj ten komponent do projektu: | ||
|
||
```bash | ||
npx shadcn@latest add form | ||
``` | ||
|
||
6. **Schemat** | ||
|
||
Aby upewnić się, że użytkownik wysyła tylko takie dane, które są wymagane, stwórzmy schemat formularza: | ||
|
||
```tsx | ||
// schemas.ts | ||
import { z } from "zod"; | ||
|
||
export const FeedbackFormSchema = z.object({ | ||
email: z.string(), | ||
content: z.string(), | ||
}); | ||
``` | ||
|
||
7. **Formularz** | ||
|
||
Następnie dodajmy formularz do naszego komponentu: | ||
|
||
```tsx | ||
// FeedbackForm.tsx | ||
"use client"; | ||
|
||
import { zodResolver } from "@hookform/resolvers/zod"; | ||
import React from "react"; | ||
import { useForm } from "react-hook-form"; | ||
import type { z } from "zod"; | ||
|
||
import { Button } from "@/components/ui/button"; | ||
import { | ||
Form, | ||
FormControl, | ||
FormField, | ||
FormItem, | ||
FormLabel, | ||
FormMessage, | ||
} from "@/components/ui/form"; | ||
import { Input } from "@/components/ui/input"; | ||
import { Textarea } from "@/components/ui/textarea"; | ||
import { FeedbackFormSchema } from "@/types/schemas"; | ||
|
||
export function FeedbackForm() { | ||
const form = useForm<z.infer<typeof FeedbackFormSchema>>({ | ||
resolver: zodResolver(FeedbackFormSchema), | ||
defaultValues: { | ||
email: "", | ||
content: "", | ||
}, | ||
}); | ||
|
||
async function onSubmit(values: z.infer<typeof FeedbackFormSchema>) { | ||
// Do something with the form values | ||
} | ||
return ( | ||
<div className="m-10 w-full rounded-md border p-5 sm:max-w-[425px]"> | ||
<h1 className="text-lg font-semibold">Formularz</h1> | ||
<Form {...form}> | ||
<form | ||
onSubmit={form.handleSubmit(onSubmit)} | ||
className="grid w-full gap-4 py-4 sm:max-w-[425px]" | ||
> | ||
<FormField | ||
control={form.control} | ||
name="email" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Adres email</FormLabel> | ||
<FormControl> | ||
<Input | ||
placeholder="[email protected]" | ||
{...field} | ||
/> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<FormField | ||
control={form.control} | ||
name="content" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Treść</FormLabel> | ||
<FormControl> | ||
<Textarea placeholder="Treść zgłoszenia" {...field} /> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<Button type="submit">Prześlij zgłoszenie</Button> | ||
</form> | ||
</Form> | ||
</div> | ||
); | ||
} | ||
``` | ||
|
||
:::tip | ||
Jest to podstawowa metoda tworzenia formularzy z `shadcn/ui` i `react-hook-form`. Jeśli nie rozumiesz tego kodu, zachęcam do odwiedzenia [dokumentacji shadcn/ui](https://ui.shadcn.com/docs/components/form). | ||
::: | ||
|
||
Efekt tego wygląda mniej więcej tak: | ||
|
||
{" "} | ||
|
||
<img src={ImageFeedback4} width={"100%"} height={"auto"} /> | ||
|
||
8. **Backend** | ||
|
||
Aby cała magia mogła się zadziać, stwórzmy `Server Action`, która będzie wysyłać dane do Google. | ||
|
||
```tsx | ||
// actions/feedback.ts | ||
"use server"; | ||
|
||
import type { z } from "zod"; | ||
|
||
import { env } from "@/env.mjs"; | ||
import { FeedbackFormSchema } from "@/types/schemas"; | ||
|
||
export const sendFeedbackForm = async ( | ||
values: z.infer<typeof FeedbackFormSchema> | ||
) => { | ||
try { | ||
FeedbackFormSchema.parse(values); | ||
|
||
// Jakaś funkcja do sprawdzania rate-limitów, czy użytkownik nie spamuje | ||
// const isSpam = await checkSpam(values); | ||
// if (isSpam) { return false } czy coś | ||
|
||
const googleFormUrl = process.env.FORMS_LINK; | ||
|
||
const formData = new URLSearchParams(); | ||
formData.append(process.env.FORMS_email, values.email); | ||
formData.append(process.env.FORMS_content, values.content); | ||
|
||
const response = await fetch(googleFormUrl, { | ||
method: "POST", | ||
body: formData, | ||
headers: { | ||
"Content-Type": "application/x-www-form-urlencoded", | ||
}, | ||
}); | ||
|
||
// Obsługa odpowiedzi | ||
if (response.ok) { | ||
return true; | ||
} else { | ||
console.error("Error while sending feedback form", response); | ||
} | ||
} catch (error) { | ||
console.error("Error while sending feedback form", error); | ||
} | ||
}; | ||
``` | ||
|
||
9. **Zdobycie potrzebnych danych** | ||
|
||
Aby uzupełnić .env o potrzebne dane, przejdź do swojego formularza Google i w górnym topbarze po prawej kliknij ikonke oka, czyli podgląd: | ||
|
||
<img src={ImageFeedback5} width={"100%"} height={"auto"} /> | ||
|
||
Następnie otwórz narzędzia deweloperskie przeglądarki i wybierz zakładkę `Elements`. Następnie inspect toolem (`Ctrl + Shift + C`) najedź na formularz i znajdź element `form`: | ||
|
||
<img src={ImageFeedback6} width={"100%"} height={"auto"} /> | ||
|
||
Skopiuj link z `action` i zapisz do zmiennej `FORMS_LINK` w env. | ||
|
||
Następnie wyszukaj `Ctrl + F` w devtoolsach (nie na stronie!) `entry.` aby znaleźć dwa pola (jeśli tyle miałeś w formularzu) i skopiuj je do odpowiednich zmiennych. Są one ułożone zwykle w dobrej kolejności: | ||
|
||
<img src={ImageFeedback7} width={"100%"} height={"auto"} /> | ||
|
||
Przykładowy `.env`: | ||
|
||
``` | ||
// .env | ||
FORMS_LINK=https://docs.google.com/forms/.../formResponse | ||
FORMS_email=entry.123456789 | ||
FORMS_content=entry.987654321 | ||
``` | ||
|
||
10. **Połączenie formularza z backendem** | ||
|
||
Na sam koniec zostało połączenie formularza z backendem. Wystarczy dodać `Server Action` do naszego formularza: | ||
|
||
```tsx {20, 32} | ||
// FeedbackForm.tsx | ||
"use client"; | ||
|
||
import { zodResolver } from "@hookform/resolvers/zod"; | ||
import React from "react"; | ||
import { useForm } from "react-hook-form"; | ||
import type { z } from "zod"; | ||
|
||
import { Button } from "@/components/ui/button"; | ||
import { | ||
Form, | ||
FormControl, | ||
FormField, | ||
FormItem, | ||
FormLabel, | ||
FormMessage, | ||
} from "@/components/ui/form"; | ||
import { Input } from "@/components/ui/input"; | ||
import { FeedbackFormSchema } from "@/types/schemas"; | ||
import { sendFeedbackForm } from "@/actions/feedback"; | ||
|
||
export function FeedbackForm() { | ||
const form = useForm<z.infer<typeof FeedbackFormSchema>>({ | ||
resolver: zodResolver(FeedbackFormSchema), | ||
defaultValues: { | ||
email: "", | ||
content: "", | ||
}, | ||
}); | ||
|
||
async function onSubmit(values: z.infer<typeof FeedbackFormSchema>) { | ||
await sendFeedbackForm(values); | ||
} | ||
return ( | ||
<div className="m-10 w-full rounded-md border p-5 sm:max-w-[425px]"> | ||
<h1 className="text-lg font-semibold">Formularz</h1> | ||
<Form {...form}> | ||
<form | ||
onSubmit={form.handleSubmit(onSubmit)} | ||
className="grid w-full gap-4 py-4 sm:max-w-[425px]" | ||
> | ||
<FormField | ||
control={form.control} | ||
name="email" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Opcja 1</FormLabel> | ||
<FormControl> | ||
<Input | ||
placeholder="np. Błąd związany z formularzem" | ||
{...field} | ||
/> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<FormField | ||
control={form.control} | ||
name="content" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Opcja 2</FormLabel> | ||
<FormControl> | ||
<Input | ||
placeholder="np. Błąd związany z formularzem" | ||
{...field} | ||
/> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<Button type="submit">Prześlij zgłoszenie</Button> | ||
</form> | ||
</Form> | ||
</div> | ||
); | ||
} | ||
``` | ||
|
||
</Steps> | ||
|
||
## That's it | ||
|
||
Na koniec możesz do tego dodać jakieś ładowanie, komunikaty o sukcesie, błędach, użyć react-query, disable przycisk i pola w czasie wysylania itp. ale to już zależy od Ciebie. Teraz możesz cieszyć się swoim własnym formularzem feedbacku, który jest szybki i przyjemny dla użytkownika. |