Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
nguyenthanhanit committed Feb 5, 2024
1 parent 6adbb07 commit 09b220b
Show file tree
Hide file tree
Showing 61 changed files with 2,200 additions and 1,371 deletions.
192 changes: 141 additions & 51 deletions app/admin/[slug]/new/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import React, {useEffect, useState} from 'react'
import ScrollToTop from "react-scroll-to-top";
import {Button} from "@nextui-org/button";
import {useForm, Controller} from "react-hook-form";
import {Controller, useForm} from "react-hook-form";
import moment from "moment/moment";
import {ToastContainer, toast} from 'react-toastify';
import {each, get, map, set} from "lodash";
import {toast, ToastContainer} from 'react-toastify';
import {cloneDeep, each, get, isNil, map, set} from "lodash";
import 'react-toastify/dist/ReactToastify.css';
import {capitalize} from "@/utils/letter";
import {capitalize, convertToSlug} from "@/utils/letter";
import DynamicComponent from "@/components/dynamic-component";
import {Spinner} from "@nextui-org/spinner";

Expand All @@ -18,60 +18,117 @@ const FIELDS: any = {
name: 'name',
label: 'Tên bài viết',
component: 'InputCustom',
required: true,
},
{
name: 'publish_at',
label: 'Ngày đăng',
component: 'DatePickerCustom',
required: true,
},
{
name: 'thumbnail',
label: 'Ảnh đại diện',
component: 'SingleFileUploadForm',
required: true,
},
{
name: 'summary',
label: 'Tóm tắt',
component: 'InputCustom',
},
{
name: 'tags',
label: 'Tags',
component: 'InputCustom',
required: true,
},
{
name: 'content',
component: 'TextEditor',
required: true,
},
],
project: [
{
name: 'category',
label: 'Loại dự án',
component: 'RadioCustom',
required: true,
options: [
{
value: 'gift',
label: 'Quà tặng doanh nghiệp'
},
{
value: 'oto',
label: 'Phụ kiện ô tô'
},
{
value: 'gift-vip',
label: 'Quà tặng cao cấp'
}
]
},
{
name: 'name',
label: 'Tên bài viết',
component: 'InputCustom',
required: true,
},
{
name: 'thumbnail',
label: 'Ảnh đại diện',
component: 'SingleFileUploadForm',
required: true,
},
{
name: 'cover',
label: 'Ảnh bìa',
component: 'SingleFileUploadForm',
required: false,
},
{
name: 'images',
label: 'Ảnh chi tiết',
component: 'MultipleUploadForm',
required: false,
},
{
name: 'content',
component: 'TextEditor',
required: true,
},
],
brand: [
{
name: 'name',
label: 'Tên bài viết',
component: 'InputCustom',
required: true,
},
{
name: 'content',
label: 'Giới thiệu',
component: 'InputCustom',
required: true,
},
{
name: 'logo',
label: 'Logo',
component: 'SingleFileUploadForm',
required: true,
},
{
name: 'thumbnail',
label: 'Ảnh bìa',
component: 'SingleFileUploadForm',
required: true,
},
],
}

const TITLES = {
news: 'Tin tức',
brand: 'Đối tác',
project: 'Dự án',
}

export default function NewPage({params}: { params: { slug: string, id: string } }) {
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
Expand All @@ -88,25 +145,26 @@ export default function NewPage({params}: { params: { slug: string, id: string }

fetchData().then(res => {
let resImages: any = {};
FIELDS[params.slug].forEach((field: { component: string; name: string; }) => {
if (!['SingleFileUploadForm', 'MultipleUploadForm'].includes(field.component)) {
let cloneRes: any = cloneDeep(res);
FIELDS[params.slug].forEach((field: { component: string; name: string; required: boolean }) => {
if (!['SingleFileUploadForm', 'MultipleUploadForm'].includes(field.component) || isNil(res[field.name])) {
return;
}

resImages[field.name] = res[field.name];

if (res[field.name]?.secure_url) {
res[field.name] = res[field.name].secure_url;
set(cloneRes, field.name, res[field.name].secure_url);

return;
}

res[field.name].forEach((img: { secure_url: string; }, index: number) => {
res[field.name][index] = img.secure_url;
set(cloneRes, field.name + '.' + index, img.secure_url);
})
})

reset(res)
reset(cloneRes)
setImages(resImages);
setLoading(false);
});
Expand All @@ -126,23 +184,23 @@ export default function NewPage({params}: { params: { slug: string, id: string }

const onSubmit = async (dataForm: any) => {
setSubmitting(true);
const formData = new FormData();
let imagesData: any = {};

FIELDS[params.slug].forEach((field: { component: string; name: string; }) => {
if (!['SingleFileUploadForm', 'MultipleUploadForm'].includes(field.component)) {
return;
}

if (typeof dataForm[field.name] === 'object' && 'arrayBuffer' in dataForm[field.name]) {
formData.append(field.name, dataForm[field.name]);
if (!isNil(dataForm[field.name]) && typeof dataForm[field.name] === 'object' && 'arrayBuffer' in dataForm[field.name]) {
imagesData[field.name] = dataForm[field.name];

return;
}

if (Array.isArray(dataForm[field.name])) {
dataForm[field.name].forEach((image: File, index: number) => {
if (typeof image === 'object' && 'arrayBuffer' in image) {
formData.append(`${field.name}.${index}`, image);
imagesData[`${field.name}.${index}`] = image;
}
})
}
Expand All @@ -154,35 +212,17 @@ export default function NewPage({params}: { params: { slug: string, id: string }
}

let oldImages: string[] = [];
// Using the FormData.entries() method to get an iterator for key-value pairs
const iterator = formData.entries();
// Using the iterators .next() method to check if there are any entries
const firstEntry = iterator.next().value;
// If the firstEntry is undefined, the FormData is empty
if (firstEntry !== undefined) {
const resUpload = await fetch("/admin/api/upload", {
method: "POST",
body: formData,
});

const {
data,
error,
}: {
data: {
urls: {};
} | null;
error: string | null;
} = await resUpload.json();

if (error || !data) {
toast.error(error || "Sorry! something went wrong.");
setSubmitting(false);

return;
}
if (Object.keys(imagesData).length > 0) {
const resUpload = await fetchDataForAllUrls(imagesData);
let resImages = {};
resUpload.forEach(res => {
resImages = {
...resImages,
...res,
}
})

each(data.urls, (url, name) => {
each(resImages, (url, name) => {
if (!url) return;

const oldImage = get(dataForm, `${name}.public_id`);
Expand All @@ -195,6 +235,7 @@ export default function NewPage({params}: { params: { slug: string, id: string }
}

dataForm.publish_at = moment(dataForm.publish_at).format("x");
dataForm.slug = convertToSlug(dataForm.name);
const res = await fetch('/admin/api', {
method: 'POST',
headers: {
Expand Down Expand Up @@ -224,6 +265,47 @@ export default function NewPage({params}: { params: { slug: string, id: string }
setSubmitting(false);
};

const fetchDataForAllUrls = async (images: object) => {
const formData = new FormData();
const promises = map(images, async (img: any, name: string) => {
formData.set("file", img);

const res = await uploadImage(formData);

return {[name]: res}
});

return await Promise.all(promises);
};

const uploadImage = async (formData: any) => {
const resUpload = await fetch("/admin/api/upload", {
method: "POST",
body: formData,
});

const {
data,
error,
}: {
data: {
urls: {
file: {}
};
} | null;
error: string | null;
} = await resUpload.json();

if (error || !data) {
toast.error(error || "Sorry! something went wrong.");
setSubmitting(false);

return false;
}

return data.urls.file;
};

if (loading) {
return <main className="grow">
<section className="relative max-w-6xl mx-auto h-screen text-center">
Expand All @@ -232,14 +314,17 @@ export default function NewPage({params}: { params: { slug: string, id: string }
</main>
}

const title = get(TITLES, params.slug);

return (
<main className="grow">
<section className="relative">
<div className="relative max-w-6xl mx-auto px-4 sm:px-6">
<div className="py-12 md:py-20">
{/* Section header */}
<div className="max-w-3xl mx-auto text-center pb-2 md:pb-2">
<h1 className="h2 mb-4 text-3xl">{params?.id ? 'Chỉnh sửa' : 'Thêm mới'}</h1>
<h1
className="h2 my-4 text-3xl">{params?.id ? 'Chỉnh sửa' : 'Thêm mới'} {title}</h1>
</div>

{/* Section content */}
Expand All @@ -256,10 +341,15 @@ export default function NewPage({params}: { params: { slug: string, id: string }
key={fieldForm.name + index}
name={fieldForm.name}
control={control}
rules={{required: true}}
render={({field}) => <DynamicComponent componentName={component}
isError={!!errors[fieldForm.name]}
label={fieldForm.label} {...field} />}
rules={{required: fieldForm.required}}
render={({field}) => <DynamicComponent
componentName={component}
isError={!!errors[fieldForm.name]}
options={fieldForm.options}
label={fieldForm.label}
fetchDataForAllUrls={fetchDataForAllUrls}
{...field}
/>}
/>
})
}
Expand Down
Loading

0 comments on commit 09b220b

Please sign in to comment.