diff --git a/package.json b/package.json
index 923e637..bb714c3 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,24 @@
"@mui/material-nextjs": "^5.15.0",
"@mui/x-data-grid": "^6.18.7",
"@mui/x-date-pickers": "^6.18.7",
- "@tiptap/react": "^2.12.0",
+ "@tiptap/core": "^2.14.0",
+ "@tiptap/extension-color": "^2.14.0",
+ "@tiptap/extension-document": "^2.14.0",
+ "@tiptap/extension-gapcursor": "^2.14.0",
+ "@tiptap/extension-highlight": "^2.14.0",
+ "@tiptap/extension-list-item": "^2.14.0",
+ "@tiptap/extension-paragraph": "^2.14.0",
+ "@tiptap/extension-table": "^2.14.0",
+ "@tiptap/extension-table-cell": "^2.14.0",
+ "@tiptap/extension-table-header": "^2.14.0",
+ "@tiptap/extension-table-row": "^2.14.0",
+ "@tiptap/extension-text": "^2.14.0",
+ "@tiptap/extension-text-align": "^2.14.0",
+ "@tiptap/extension-text-style": "^2.14.0",
+ "@tiptap/extension-underline": "^2.14.0",
+ "@tiptap/pm": "^2.14.0",
+ "@tiptap/react": "^2.14.0",
+ "@tiptap/starter-kit": "^2.14.0",
"@unly/universal-language-detector": "^2.0.3",
"apexcharts": "^3.45.2",
"axios": "^1.9.0",
@@ -28,6 +45,7 @@
"i18next": "^23.7.11",
"i18next-resources-to-backend": "^1.2.0",
"lodash": "^4.17.21",
+ "mui-color-input": "^7.0.0",
"next": "14.0.4",
"next-auth": "^4.24.5",
"next-pwa": "^5.6.0",
diff --git a/src/app/(main)/settings/importTesting/page.tsx b/src/app/(main)/settings/importTesting/page.tsx
new file mode 100644
index 0000000..ca07e8b
--- /dev/null
+++ b/src/app/(main)/settings/importTesting/page.tsx
@@ -0,0 +1,30 @@
+import ImportTesting from "@/components/ImportTesting";
+import { getServerI18n } from "@/i18n";
+import { Stack } from "@mui/material";
+import { Metadata } from "next";
+import React, { Suspense } from "react";
+
+export const metadata: Metadata = {
+ title: "Import Testing"
+}
+
+const ImportTestingPage: React.FC = async () => {
+ const { t } = await getServerI18n("importTesting");
+
+ return (
+ <>
+
+
+ }>
+
+
+ >
+ )
+}
+
+export default ImportTestingPage;
\ No newline at end of file
diff --git a/src/app/api/settings/importTesting/actions.ts b/src/app/api/settings/importTesting/actions.ts
new file mode 100644
index 0000000..20db792
--- /dev/null
+++ b/src/app/api/settings/importTesting/actions.ts
@@ -0,0 +1,21 @@
+"use server";
+
+import { serverFetchWithNoContent } from '@/app/utils/fetchUtil';
+import { BASE_API_URL } from "@/config/api";
+
+export interface ImportPoForm {
+ dateFrom: string,
+ dateTo: string,
+}
+
+export interface ImportTestingForm {
+ po: ImportPoForm
+}
+
+export const testImportPo = async (data: ImportPoForm) => {
+ return serverFetchWithNoContent(`${BASE_API_URL}/m18/po`, {
+ method: "POST",
+ body: JSON.stringify(data),
+ headers: { "Content-Type": "application/json" },
+ })
+}
\ No newline at end of file
diff --git a/src/app/api/settings/importTesting/index.ts b/src/app/api/settings/importTesting/index.ts
new file mode 100644
index 0000000..f77bda3
--- /dev/null
+++ b/src/app/api/settings/importTesting/index.ts
@@ -0,0 +1 @@
+// "server only"
\ No newline at end of file
diff --git a/src/app/utils/formatUtil.ts b/src/app/utils/formatUtil.ts
index a882bbc..206f0e3 100644
--- a/src/app/utils/formatUtil.ts
+++ b/src/app/utils/formatUtil.ts
@@ -49,6 +49,11 @@ export const dateStringToDayjs = (date: string) => {
return dayjs(date, OUTPUT_DATE_FORMAT)
}
+export const dateTimeStringToDayjs = (dateTime: string) => {
+ // Format: YYYY/MM/DD HH:mm:ss
+ return dayjs(dateTime, `${OUTPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`)
+}
+
export const stockInLineStatusMap: { [status: string]: number } = {
"draft": 0,
"pending": 1,
diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx
index 65f399c..863c5e2 100644
--- a/src/components/Breadcrumb/Breadcrumb.tsx
+++ b/src/components/Breadcrumb/Breadcrumb.tsx
@@ -19,6 +19,7 @@ const pathToLabelMap: { [path: string]: string } = {
"/scheduling/detail": "Detail Scheduling",
"/scheduling/detail/edit": "FG Production Schedule",
"/inventory": "Inventory",
+ "/settings/importTesting": "Import Testing",
};
const Breadcrumb = () => {
diff --git a/src/components/ImportTesting/ImportPo.tsx b/src/components/ImportTesting/ImportPo.tsx
new file mode 100644
index 0000000..052676f
--- /dev/null
+++ b/src/components/ImportTesting/ImportPo.tsx
@@ -0,0 +1,127 @@
+"use client"
+import { ImportTestingForm } from "@/app/api/settings/importTesting/actions";
+import { ImportPoForm } from "@/app/api/settings/importTesting/actions";
+import { INPUT_DATE_FORMAT, OUTPUT_DATE_FORMAT, OUTPUT_TIME_FORMAT, dateTimeStringToDayjs } from "@/app/utils/formatUtil";
+import { Check } from "@mui/icons-material";
+import { Box, Button, Card, CardContent, FormControl, Grid, Stack, Typography } from "@mui/material";
+import { DatePicker, DateTimePicker, LocalizationProvider } from "@mui/x-date-pickers";
+import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
+import dayjs, { Dayjs } from "dayjs";
+import React, { useCallback, useState } from "react";
+import { Controller, useFormContext } from "react-hook-form";
+import { useTranslation } from "react-i18next";
+
+interface Props {
+}
+
+const ImportPo: React.FC = ({
+}) => {
+
+ const { t } = useTranslation()
+ const [isLoading, setIsLoading] = useState(false)
+ const {
+ control,
+ formState: { errors },
+ watch
+ } = useFormContext()
+
+ const handleDateTimePickerOnChange = useCallback((value: Dayjs | null, onChange: (value: any) => void) => {
+ const formattedValue = value ? value.format(`${INPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`) : null
+ onChange(formattedValue)
+ }, [])
+
+
+
+ return (
+
+
+
+
+ {t("Import PO")}
+
+
+
+
+
+ value && dateTimeStringToDayjs(value).isValid() ? true : "Invalid date-time"
+ },
+ }}
+ render={({ field, fieldState: { error } }) => (
+ (handleDateTimePickerOnChange(newValue, field.onChange))}
+ slotProps={{
+ textField: {
+ error: !!error,
+ helperText: error ? error.message : null
+ }
+ }}
+ />
+ )}
+ />
+
+ {"-"}
+
+
+ value && dateTimeStringToDayjs(value).isValid() ? true : "Invalid date-time",
+ isFuture: (value) =>
+ dateTimeStringToDayjs(value).isAfter(watch("po.dateFrom")) || "Date must be in the future",
+ },
+ }}
+ render={({ field, fieldState: { error } }) => (
+ (handleDateTimePickerOnChange(newValue, field.onChange))}
+ slotProps={{
+ textField: {
+ error: !!error,
+ helperText: error ? error.message : null
+ }
+ }}
+ />
+ )}
+ />
+
+
+
+
+
+ }
+ type="submit"
+ >
+ {t("Import Po")}
+
+
+
+
+ )
+}
+
+export default ImportPo;
\ No newline at end of file
diff --git a/src/components/ImportTesting/ImportTesting.tsx b/src/components/ImportTesting/ImportTesting.tsx
new file mode 100644
index 0000000..f9b7ad1
--- /dev/null
+++ b/src/components/ImportTesting/ImportTesting.tsx
@@ -0,0 +1,67 @@
+"use client"
+
+import { ImportTestingForm, testImportPo } from "@/app/api/settings/importTesting/actions";
+import { Card, CardContent, Grid, Stack, Typography } from "@mui/material";
+import React, { BaseSyntheticEvent, FormEvent, useCallback, useState } from "react";
+import { FormProvider, SubmitErrorHandler, useForm } from "react-hook-form";
+import { useTranslation } from "react-i18next";
+import ImportPo from "./ImportPo";
+import { ImportPoForm } from "@/app/api/settings/importTesting/actions";
+
+interface Props {
+
+}
+
+const ImportTesting: React.FC = ({
+
+}) => {
+
+ const { t } = useTranslation()
+ const [isLoading, setIsLoading] = useState(false)
+ const formProps = useForm()
+
+ const onSubmit = useCallback(async (data: ImportTestingForm, event?: BaseSyntheticEvent) => {
+ const buttonId = (event?.nativeEvent as SubmitEvent).submitter?.id
+ console.log(data.po)
+ switch (buttonId) {
+ case "importPo":
+ setIsLoading(() => true)
+ const response = await testImportPo(data.po)
+ console.log(response)
+ if (response) {
+ setIsLoading(() => false)
+ }
+ break;
+ default:
+ break;
+ }
+ }, [])
+
+ const onSubmitError = useCallback>(
+ (errors) => {
+ console.log(errors)
+ },
+ [],
+ );
+
+ return (
+
+
+ {t("Status: ")}{isLoading ? t("Importing...") : t("Ready to import")}
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ImportTesting;
\ No newline at end of file
diff --git a/src/components/ImportTesting/ImportTestingWrapper.tsx b/src/components/ImportTesting/ImportTestingWrapper.tsx
new file mode 100644
index 0000000..843f4f1
--- /dev/null
+++ b/src/components/ImportTesting/ImportTestingWrapper.tsx
@@ -0,0 +1,17 @@
+import React from "react";
+import GeneralLoading from "../General/GeneralLoading"
+import ImportTesting from "./ImportTesting";
+
+interface SubComponents {
+ Loading: typeof GeneralLoading;
+}
+
+const ImportTestingWrapper: React.FC & SubComponents = async () => {
+
+ return
+}
+
+
+ImportTestingWrapper.Loading = GeneralLoading;
+
+export default ImportTestingWrapper
\ No newline at end of file
diff --git a/src/components/ImportTesting/index.ts b/src/components/ImportTesting/index.ts
new file mode 100644
index 0000000..de60105
--- /dev/null
+++ b/src/components/ImportTesting/index.ts
@@ -0,0 +1 @@
+export { default } from './ImportTestingWrapper'
\ No newline at end of file
diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx
index 5de05b0..ca99d64 100644
--- a/src/components/NavigationContent/NavigationContent.tsx
+++ b/src/components/NavigationContent/NavigationContent.tsx
@@ -263,6 +263,11 @@ const NavigationContent: React.FC = () => {
label: "Mail",
path: "/settings/mail",
},
+ {
+ icon: ,
+ label: "Import Testing",
+ path: "/settings/importTesting",
+ },
],
},
];