Skip to content

Commit

Permalink
add: tickets DB, feature: submitTicket page
Browse files Browse the repository at this point in the history
  • Loading branch information
BlackRedStudio committed Dec 13, 2023
1 parent 328dcd8 commit 1258782
Show file tree
Hide file tree
Showing 16 changed files with 739 additions and 12 deletions.
4 changes: 2 additions & 2 deletions app/(authenticated)/profile/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ async function profilePage() {
if (!user) return null;

return (
<>
<section>
<Profile user={user} />
</>
</section>
);
}

Expand Down
18 changes: 18 additions & 0 deletions app/(authenticated)/ticket/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import TicketForm from '@/components/modules/TicketForm';
import H2 from '@/components/ui/H2';

function ticketPage() {
return (
<section>
<H2 className="mb-3">Zgłoś uwagę lub pomysł</H2>
<p>
Za pomocą poniższego formularza, możesz zgłosić wszelkie
znalezione błedy, a także podzielić się swoimi przemyśleniami,
sugestiami lub nowymi pomysłami dotyczącymi naszej aplikacji.
</p>
<TicketForm />
</section>
);
}

export default ticketPage;
8 changes: 4 additions & 4 deletions app/delete-account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ async function deleteAccountPage() {
className="text-orange">
[email protected]
</a>{' '}
zawierającą hasło,{' '}
zawierającą frazę,{' '}
<strong>
proszę o usunięcie mojego konta z aplikacji Mniam App
</strong>{' '}
lub podobne hasło. Wszystkie Twoje dane oraz lista produktów
lub podobną. Wszystkie Twoje dane oraz lista produktów
i ich oceny zostaną usunięte w ciągu najbliższych 5 dni
roboczych.
<br />
<br />
Kasowanie konta możesz odwołać w dowolnym momencie o ile
jeszcze nie doszło do jego kasacji, wtedy należy wysłać na
podany wcześniej adres email wiadomość z hasłem:{' '}
podany wcześniej adres email wiadomość z frazą:{' '}
<strong>
proszę jednak o nie usuwanie mojego konta z aplikacji
Mniam App
</strong>{' '}
lub podobnym.
lub podobną.
</H3>
</main>
</ScrollArea>
Expand Down
3 changes: 3 additions & 0 deletions components/layout/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ function Menu() {
className="ml-3"
/>
</DropdownMenuItem>
<Link href="/ticket">
<DropdownMenuItem>Zgłoś uwagę/pomysł</DropdownMenuItem>
</Link>
<AlertModal
title="Czy napewno chcesz się wylogować?"
accept={async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,13 @@ function ProductVerificationCard({
{loading && <Loader className="mt-4" />}
<Button
className="w-full"
disabled={loading}
onClick={() => handleAcceptProduct()}>
Akceptuj produkt
</Button>
<Button
className="w-full"
disabled={loading}
onClick={() => handleRejectVerification()}
variant={'destructive'}>
Odrzuć produkt
Expand Down
10 changes: 5 additions & 5 deletions components/modules/Profile/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function Profile({ user }: TProfile) {
defaultValue={user.name ?? user.email}
required
/>
{<FormError formErrors={formErrors?.name} />}
<FormError formErrors={formErrors?.name} />
</div>
<div className="mb-4">
<Label htmlFor="email">Email</Label>
Expand All @@ -93,7 +93,7 @@ function Profile({ user }: TProfile) {
defaultValue={user.email}
required
/>
{<FormError formErrors={formErrors?.email} />}
<FormError formErrors={formErrors?.email} />
</div>
<div className="relative mb-4">
<Label htmlFor="password">Hasło</Label>
Expand All @@ -103,7 +103,7 @@ function Profile({ user }: TProfile) {
className="mt-1"
placeholder="Wpisz swoje hasło"
/>
{<FormError formErrors={formErrors?.password} />}
<FormError formErrors={formErrors?.password} />
{showPassword ? (
<Icons.eye
className="absolute bottom-[7px] right-[7px]"
Expand All @@ -124,7 +124,7 @@ function Profile({ user }: TProfile) {
className="mt-1"
placeholder="Wpisz ponownie swoje hasło"
/>
{<FormError formErrors={formErrors?.passwordConfirm} />}
<FormError formErrors={formErrors?.passwordConfirm} />
{showPassword ? (
<Icons.eye
className="absolute bottom-[7px] right-[7px]"
Expand All @@ -146,7 +146,7 @@ function Profile({ user }: TProfile) {
className="mt-1"
accept="image/jpeg, image/jpg"
/>
{<FormError formErrors={formErrors?.image} />}
<FormError formErrors={formErrors?.image} />
</div>
{user.image && (
<Button className="mb-5" onClick={e => handleDeleteAvatar(e)}>
Expand Down
140 changes: 140 additions & 0 deletions components/modules/TicketForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
'use client';

import { ChangeEvent, useState } from 'react';
import Image from 'next/image';
import FileResizer from 'react-image-file-resizer';

import { useToast } from '@/lib/hooks/use-toast';
import { TTicketValidatorErrors } from '@/lib/validators/ticket-validator';

import { Button } from '../ui/Button';
import FormError from '../ui/FormError';
import { Input } from '../ui/Input';
import { Label } from '../ui/Label';
import { Textarea } from '../ui/Textarea';
import Loader from '../ui/Loader';
import { useRouter } from 'next/navigation';
import { submitTicket__Action } from '@/server/actions/user-actions';

function TicketForm() {
const { toast } = useToast();

const router = useRouter();
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState('');
const [formErrors, setFormErrors] = useState<TTicketValidatorErrors>();
const [attachment, setAttachment] = useState<File | null>(null);

const submitTicket = async () => {

setLoading(true);

const formData = new FormData();
formData.append('message', message);

if(attachment) {
formData.append('attachment', attachment);
}

const res = await submitTicket__Action(formData);

if (!res.success && res.errors) {
setFormErrors(res.errors);
}

toast({
title: res.message,
variant: res.success ? 'success' : 'destructive',
});

setLoading(false);

if (res.success) {
router.push('/dashboard');
}
};

const handleSelectImage = (e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.files || e.target.files.length === 0) {
setAttachment(null);
return;
}
const file = e.target.files[0];

try {
FileResizer.imageFileResizer(
file,
900,
900,
'JPEG',
79,
0,
file => {
if (file instanceof File) {
setAttachment(file);
} else {
throw new Error('Zły format pliku');
}
},
'file',
100,
100,
);
} catch (err) {
toast({
title: 'Problem z plikiem, spróbuj ponownie lub zmień na inny plik.',
variant: 'destructive',
});
}
};

return (
<form className="mt-5 space-y-3">
{attachment && (
<>
<div className="mt-5 text-center text-success">
Podgląd obrazka
</div>
<div className="relative h-[300px] w-full">
<Image
src={URL.createObjectURL(attachment)}
fill
className="object-contain"
alt="Pogląd obrazka"
/>
</div>
</>
)}
<Label>Opcjonalnie dołącz screenshot lub obrazek</Label>
<Input
type="file"
name="attachment"
className="mt-5"
onChange={handleSelectImage}
accept="image/*"
/>
<FormError className="mb-2" formErrors={formErrors?.attachment} />
<Textarea
name="message"
placeholder="Opisz czego dokładnie dotyczy zgłoszenie."
className="min-h-[150px]"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<FormError formErrors={formErrors?.message} />
{loading && <Loader className="mt-4" />}
<Button onClick={submitTicket} className="w-full" disabled={loading}>Wyślij</Button>
<p className="text-sm text-muted-foreground">
Zgłoszenia można przesyłać także na adres email{' '}
<strong>
<a href="mailto:[email protected]">
[email protected]
</a>
</strong>
.
</p>
</form>
);
}

export default TicketForm;
24 changes: 24 additions & 0 deletions components/ui/Textarea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from "react"

import { cn } from "@/lib/utils/utils"

export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}

const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = "Textarea"

export { Textarea }
9 changes: 9 additions & 0 deletions drizzle/0026_overjoyed_meteorite.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE `tickets` (
`id` varchar(255) NOT NULL,
`userId` varchar(255) NOT NULL,
`subject` varchar(255),
`message` text DEFAULT (''),
`attachment` varchar(255),
`dateCreated` timestamp(3) DEFAULT (now()),
CONSTRAINT `tickets_id` PRIMARY KEY(`id`)
);
Loading

0 comments on commit 1258782

Please sign in to comment.