Skip to content

Commit 7b850ef

Browse files
committed
feat(create-event): finishing wiring update event
1 parent 47f2d95 commit 7b850ef

File tree

10 files changed

+159
-50
lines changed

10 files changed

+159
-50
lines changed

src/domains/Events.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ export const eventSchema = z.object({
44
id: z.number(),
55
title: z.string(),
66
description: z.string(),
7-
// content: z.string().optional(),
87
slug: z.string().optional(),
98
author: z.string(),
109
image: z.string(),
@@ -84,11 +83,11 @@ export const createEventFormSchema = (t: (key: string) => string) =>
8483
z.object({
8584
title: z.string().min(1, t("validation.title-required")),
8685
description: z.string().min(1, t("validation.description-required")),
87-
file_name: z.string().optional(),
86+
// file_name: z.string().optional(),
8887
slug: z.string().min(1, t("validation.slug-required")),
89-
is_online: z.boolean(),
9088
date: z.string().min(1, t("validation.date-required")),
9189
type: z.string().min(1, t("validation.type-required")),
90+
session_type: z.string().min(1, t("validation.session-type-required")),
9291
location: z.string().min(1, t("validation.location-required")),
9392
duration: z.string().min(1, t("validation.duration-required")),
9493
status: z.string().min(1, t("validation.status-required")),
@@ -99,6 +98,14 @@ export const createEventFormSchema = (t: (key: string) => string) =>
9998
speakers: z.array(z.string()),
10099
reservation_start_date: z.string().min(1, t("validation.reservation-start-required")),
101100
reservation_end_date: z.string().min(1, t("validation.reservation-end-required")),
101+
image: z.union([
102+
z.instanceof(File).refine((file) => ["image/png", "image/jpeg", "image/jpg", "image/webp"].includes(file.type), {
103+
message: t("validation.image-type"),
104+
}),
105+
z.string().min(1, t("validation.image-required")),
106+
]),
102107
});
103108

104109
export type EventFormType = z.infer<ReturnType<typeof createEventFormSchema>>;
110+
111+
export type CreateEventPayload = Omit<EventFormType, "image"> & { file_name: string };

src/features/events/components/EventForm.tsx

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { useForm } from "react-hook-form";
44
import { zodResolver } from "@hookform/resolvers/zod";
55
import { format } from "date-fns";
6-
import { CalendarIcon, Save, X } from "lucide-react";
6+
import { CalendarIcon, Save, X, Upload } from "lucide-react";
77
import { useTranslations } from "next-intl";
88
import { Button } from "@/components/ui/Button";
99
import { Input } from "@/components/ui/Input";
@@ -12,11 +12,10 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "
1212
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/Popover";
1313
import { Calendar } from "@/components/ui/Calendar";
1414
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/Select";
15-
import { Switch } from "@/components/ui/Switch";
1615
import TextEditor from "@/components/common/TextEditor/TextEditor";
1716
import { cn, generateSlug } from "@/lib/utils";
1817
import { createEventFormSchema, EventFormType } from "@/domains/Events";
19-
import { eventTypes, eventStatuses } from "../constants";
18+
import { eventTypes, eventStatuses, sessionTypes } from "../constants";
2019
import { useState, useEffect } from "react";
2120
import Badge from "@/components/ui/Badge";
2221

@@ -39,21 +38,21 @@ const EventForm = ({ onSubmit, isLoading = false, initialData, mode = "create" }
3938
defaultValues: initialData || {
4039
title: "",
4140
description: "",
42-
file_name: "",
4341
slug: "",
44-
is_online: false,
4542
date: "",
4643
type: "",
44+
session_type: "",
4745
location: "",
4846
duration: "",
49-
status: "coming soon",
47+
status: "soon",
5048
capacity: 0,
5149
price: 0,
5250
registration_link: "",
5351
tags: [],
5452
speakers: [],
5553
reservation_start_date: "",
5654
reservation_end_date: "",
55+
image: "",
5756
},
5857
});
5958

@@ -99,7 +98,6 @@ const EventForm = ({ onSubmit, isLoading = false, initialData, mode = "create" }
9998
};
10099

101100
const handleSubmit = (data: EventFormType) => {
102-
console.log("dataaaa", data);
103101
onSubmit(data);
104102
};
105103

@@ -159,7 +157,32 @@ const EventForm = ({ onSubmit, isLoading = false, initialData, mode = "create" }
159157
)}
160158
/>
161159

162-
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
160+
<FormField
161+
control={form.control}
162+
name="image"
163+
render={({ field }) => (
164+
<FormItem>
165+
<FormLabel aria-required>{t("labels.image")}</FormLabel>
166+
<FormControl>
167+
<div className="flex items-center gap-4">
168+
<Input
169+
type="file"
170+
accept="image/*"
171+
onChange={(e) => {
172+
const file = e.target.files?.[0];
173+
field.onChange(file);
174+
}}
175+
className="file:bg-primary file:text-primary-foreground hover:file:bg-primary/90 file:mr-4 file:rounded-md file:border-0 file:px-4 file:py-2 file:text-sm file:font-medium"
176+
/>
177+
<Upload className="text-muted-foreground h-4 w-4" />
178+
</div>
179+
</FormControl>
180+
<FormMessage />
181+
</FormItem>
182+
)}
183+
/>
184+
185+
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
163186
<FormField
164187
control={form.control}
165188
name="type"
@@ -185,6 +208,31 @@ const EventForm = ({ onSubmit, isLoading = false, initialData, mode = "create" }
185208
)}
186209
/>
187210

211+
<FormField
212+
control={form.control}
213+
name="session_type"
214+
render={({ field }) => (
215+
<FormItem>
216+
<FormLabel aria-required>{t("labels.session-type")}</FormLabel>
217+
<Select onValueChange={field.onChange} defaultValue={field.value}>
218+
<FormControl>
219+
<SelectTrigger>
220+
<SelectValue placeholder={t("placeholders.session-type")} />
221+
</SelectTrigger>
222+
</FormControl>
223+
<SelectContent>
224+
{sessionTypes.map((sessionType) => (
225+
<SelectItem key={sessionType.value} value={sessionType.value}>
226+
{sessionType.label}
227+
</SelectItem>
228+
))}
229+
</SelectContent>
230+
</Select>
231+
<FormMessage />
232+
</FormItem>
233+
)}
234+
/>
235+
188236
<FormField
189237
control={form.control}
190238
name="status"
@@ -465,22 +513,6 @@ const EventForm = ({ onSubmit, isLoading = false, initialData, mode = "create" }
465513
)}
466514
/>
467515

468-
<FormField
469-
control={form.control}
470-
name="is_online"
471-
render={({ field }) => (
472-
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
473-
<div className="space-y-0.5">
474-
<FormLabel className="text-base">{t("labels.online-event")}</FormLabel>
475-
<div className="text-muted-foreground text-sm">{t("labels.online-description")}</div>
476-
</div>
477-
<FormControl>
478-
<Switch checked={field.value} onCheckedChange={field.onChange} />
479-
</FormControl>
480-
</FormItem>
481-
)}
482-
/>
483-
484516
<div className="flex gap-4 pt-4">
485517
<Button type="submit" className="bg-hmc-base-blue hover:bg-hmc-base-blue/90" disabled={isLoading}>
486518
<Save className="mr-2 h-4 w-4" />

src/features/events/constants.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,21 @@ export const mockEvents: TechEvent[] = [
114114

115115
export const eventTypes = [
116116
{ value: "Tech Talk", label: "Tech Talk" },
117-
{ value: "Workshop", label: "Workshop" },
118-
{ value: "Seminar", label: "Seminar" },
119117
{ value: "Conference", label: "Conference" },
120-
{ value: "Bootcamp", label: "Bootcamp" },
118+
{ value: "Ngobar", label: "Ngobar" },
121119
];
122120

123121
export const eventStatuses = [
124-
{ value: "coming soon", label: "Coming Soon" },
122+
{ value: "soon", label: "Soon" },
125123
{ value: "open", label: "Open" },
126124
{ value: "closed", label: "Closed" },
127-
{ value: "cancelled", label: "Cancelled" },
125+
// { value: "cancelled", label: "Cancelled" },
126+
];
127+
128+
export const sessionTypes = [
129+
{ value: "online", label: "Online" },
130+
{ value: "offline", label: "Offline" },
131+
{ value: "hybrid", label: "Hybrid" },
128132
];
129133

130134
export const eventsInfo: EventInfoType[] = [

src/features/events/hooks/useEvent.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
import { useQuery } from "@tanstack/react-query";
1+
import { useQuery, useMutation } from "@tanstack/react-query";
22
import { eventsService } from "@/services/events";
3+
import { uploadsService } from "@/services/uploads";
4+
import { EventFormType, CreateEventPayload } from "@/domains/Events";
5+
import { toast } from "sonner";
6+
import { useRouter } from "@/lib/navigation";
37

48
export const useEventById = (eventId: string) => {
59
return useQuery({
@@ -41,3 +45,37 @@ export const useEventsAdmin = (page: number, limit: number, search?: string) =>
4145
},
4246
});
4347
};
48+
49+
export const useCreateEvent = (t: (key: string) => string) => {
50+
const router = useRouter();
51+
52+
const submitMutation = useMutation({
53+
mutationKey: ["createEvent"],
54+
mutationFn: (payload: CreateEventPayload) => eventsService.createEventAdmin(payload),
55+
onSuccess: () => {
56+
toast.success(t("EventForm.create-success"));
57+
router.push("/admin/events");
58+
},
59+
});
60+
61+
const createMutation = useMutation({
62+
mutationFn: (payload: EventFormType) => uploadsService.uploadImageAdmin(payload.image, "events"),
63+
onSuccess: (data, variables) => {
64+
const filename = data.data.file_name;
65+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
66+
const { image, ...rest } = variables;
67+
submitMutation.mutate({
68+
...rest,
69+
file_name: filename,
70+
});
71+
},
72+
});
73+
74+
const isLoading = createMutation.isPending || submitMutation.isPending;
75+
76+
return {
77+
createMutation,
78+
submitMutation,
79+
isLoading,
80+
};
81+
};

src/features/events/hooks/useRegistEvent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const useRegistEvent = (data: EventType) => {
3131
console.log("image proof payment", image_proof_payment);
3232
const {
3333
data: { file_name: uploadedImageFileName },
34-
} = await uploadsService.uploadImage(image_proof_payment as File, "payment", "event");
34+
} = await uploadsService.uploadImage(image_proof_payment as File, "event");
3535

3636
setNameImage(uploadedImageFileName);
3737
registPayload = {

src/features/events/pages/AdminEventsCreatePage.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

33
import { EventFormType } from "@/domains/Events";
44
import EventForm from "../components/EventForm";
5+
import { useCreateEvent } from "../hooks/useEvent";
6+
import { useTranslations } from "next-intl";
57

68
const AdminEventsCreatePage = () => {
9+
const t = useTranslations();
10+
const { createMutation, isLoading } = useCreateEvent(t);
11+
712
const handleSubmit = (data: EventFormType) => {
8-
console.log(data);
13+
createMutation.mutate(data);
914
};
1015

1116
return (
@@ -15,7 +20,7 @@ const AdminEventsCreatePage = () => {
1520
<p className="text-muted-foreground">Add a new event or workshop</p>
1621
</div>
1722

18-
<EventForm onSubmit={handleSubmit} mode="create" />
23+
<EventForm onSubmit={handleSubmit} mode="create" isLoading={isLoading} />
1924
</section>
2025
);
2126
};

src/locales/en.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,12 @@
301301
"price-min": "Price must be 0 or greater",
302302
"registration-url": "Must be a valid URL",
303303
"reservation-start-required": "Reservation start date is required",
304-
"reservation-end-required": "Reservation end date is required"
304+
"reservation-end-required": "Reservation end date is required",
305+
"session-type-required": "Session type is required",
306+
"image-type": "Please select a valid image file (PNG, JPEG, JPG, WEBP)",
307+
"image-required": "Image is required"
305308
},
309+
"create-success": "Event created successfully!",
306310
"labels": {
307311
"title": "Event Title",
308312
"description": "Description",
@@ -319,8 +323,8 @@
319323
"registration-link": "Registration Link",
320324
"tags": "Tags",
321325
"speakers": "Speakers",
322-
"online-event": "Online Event",
323-
"online-description": "Is this an online event?"
326+
"session-type": "Session Type",
327+
"image": "Event Image"
324328
},
325329
"placeholders": {
326330
"title": "Enter event title",
@@ -334,7 +338,8 @@
334338
"price": "Enter price",
335339
"registration-link": "https://example.com/register",
336340
"tags": "Add a tag",
337-
"speakers": "Add a speaker"
341+
"speakers": "Add a speaker",
342+
"session-type": "Select session type"
338343
},
339344
"buttons": {
340345
"add": "Add",

src/locales/id.json

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,12 @@
301301
"price-min": "Harga minimal 0 atau gratis",
302302
"registration-url": "Harus berupa URL yang valid",
303303
"reservation-start-required": "Tanggal mulai pendaftaran wajib diisi",
304-
"reservation-end-required": "Tanggal berakhir pendaftaran wajib diisi"
304+
"reservation-end-required": "Tanggal berakhir pendaftaran wajib diisi",
305+
"session-type-required": "Jenis sesi wajib dipilih",
306+
"image-type": "Pilih file gambar yang valid (PNG, JPEG, JPG, WEBP)",
307+
"image-required": "Gambar wajib diunggah"
305308
},
309+
"create-success": "Event berhasil dibuat!",
306310
"labels": {
307311
"title": "Judul Acara",
308312
"description": "Deskripsi",
@@ -319,8 +323,8 @@
319323
"registration-link": "Link Pendaftaran",
320324
"tags": "Tag",
321325
"speakers": "Pembicara",
322-
"online-event": "Acara Online",
323-
"online-description": "Apakah ini acara online?"
326+
"session-type": "Jenis Sesi",
327+
"image": "Gambar Acara"
324328
},
325329
"placeholders": {
326330
"title": "Masukkan judul acara",
@@ -334,7 +338,8 @@
334338
"price": "Masukkan harga",
335339
"registration-link": "https://contoh.com/daftar",
336340
"tags": "Tambahkan tag",
337-
"speakers": "Tambahkan pembicara"
341+
"speakers": "Tambahkan pembicara",
342+
"session-type": "Pilih jenis sesi"
338343
},
339344
"buttons": {
340345
"add": "Tambah",

src/services/events/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { HttpResponse } from "@/types/http";
22
import { fetcher } from "../instance";
3-
import { EventType, RegistrationForm, UserEventType } from "@/domains/Events";
3+
import { CreateEventPayload, EventType, RegistrationForm, UserEventType } from "@/domains/Events";
44

55
export const eventsService = {
66
/**
@@ -56,7 +56,7 @@ export const eventsService = {
5656
});
5757
},
5858

59-
// async createEventAdmin(payload): Promise<any> {
60-
// return fetcher.get(`/admin/events`, payload);
61-
// },
59+
async createEventAdmin(payload: CreateEventPayload): Promise<HttpResponse<null>> {
60+
return fetcher.post(`/admin/events`, payload);
61+
},
6262
};

0 commit comments

Comments
 (0)