| @@ -2,10 +2,11 @@ | |||
| import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | |||
| import { BASE_API_URL } from "@/config/api"; | |||
| import { MailSetting } from "."; | |||
| import { MailSetting, MailTemplate } from "."; | |||
| export interface MailSave { | |||
| settings: MailSetting[]; | |||
| templates: MailTemplate[]; | |||
| // template: MailTemplate; | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
| import { BASE_API_URL } from "@/config/api"; | |||
| import { cache } from "react"; | |||
| import "server-only"; | |||
| export interface MailSMTP { | |||
| host: string; | |||
| @@ -17,6 +18,20 @@ export interface MailSetting { | |||
| type: string; | |||
| } | |||
| export interface MailTemplate { | |||
| id: number; | |||
| to: string; | |||
| cc: string; | |||
| code: string; | |||
| description: string; | |||
| type: string; | |||
| params: string; | |||
| subjectCht: string; | |||
| subjectEng: string; | |||
| contentCht: string; | |||
| contentEng: string; | |||
| } | |||
| // export interface MailTemplate { | |||
| // cc?: string; | |||
| // bcc?: string; | |||
| @@ -26,6 +41,7 @@ export interface MailSetting { | |||
| export const preloadMails = () => { | |||
| fetchMailSetting(); | |||
| fetchMailTemplates(); | |||
| // fetchMailTimesheetTemplate(); | |||
| }; | |||
| @@ -35,6 +51,12 @@ export const fetchMailSetting = cache(async () => { | |||
| }); | |||
| }); | |||
| export const fetchMailTemplates = cache(async () => { | |||
| return serverFetchJson<MailTemplate[]>(`${BASE_API_URL}/mailTemplates`, { | |||
| next: { tags: ["mailTemplates"] }, | |||
| }); | |||
| }); | |||
| // export const fetchMailTimesheetTemplate = cache(async () => { | |||
| // return serverFetchJson<MailSetting[]>(`${BASE_API_URL}/mails/timesheet-template`, { | |||
| // next: { tags: ["mailTimesheetTemplate"] }, | |||
| @@ -21,7 +21,7 @@ | |||
| overflow: hidden; | |||
| /* palette.neutral[200] */ | |||
| transition: border-color 0.3s, box-shadow 0.3s; | |||
| border-color: #F04438; | |||
| /* palette.primary.main */ | |||
| box-shadow: #F04438 0 0 0 2px; | |||
| @@ -41,7 +41,7 @@ | |||
| display: none; | |||
| } | |||
| :not(.tiptap-error) > .tiptap:focus { | |||
| :not(.tiptap-error)>.tiptap:focus { | |||
| background-color: transparent; | |||
| border-color: #8dba00; | |||
| /* palette.primary.main */ | |||
| @@ -53,6 +53,74 @@ | |||
| outline: none; | |||
| } | |||
| /* Basic editor styles */ | |||
| .tiptap { | |||
| :first-child { | |||
| margin-top: 0; | |||
| } | |||
| /* Table-specific styling */ | |||
| table { | |||
| border-collapse: collapse; | |||
| margin: 0; | |||
| overflow: hidden; | |||
| table-layout: fixed; | |||
| width: 100%; | |||
| td, | |||
| th { | |||
| border: 1px solid #000000; | |||
| box-sizing: border-box; | |||
| min-width: 1em; | |||
| padding: 6px 8px; | |||
| position: relative; | |||
| vertical-align: top; | |||
| >* { | |||
| margin-bottom: 0; | |||
| } | |||
| } | |||
| th { | |||
| background-color: #939393; | |||
| font-weight: bold; | |||
| text-align: left; | |||
| } | |||
| .selectedCell:after { | |||
| background: rgba(180, 180, 180, 0.467); | |||
| content: ""; | |||
| left: 0; | |||
| right: 0; | |||
| top: 0; | |||
| bottom: 0; | |||
| pointer-events: none; | |||
| position: absolute; | |||
| z-index: 2; | |||
| } | |||
| .column-resize-handle { | |||
| background-color: #d000ff; | |||
| bottom: -2px; | |||
| pointer-events: none; | |||
| position: absolute; | |||
| right: -2px; | |||
| top: 0; | |||
| width: 4px; | |||
| } | |||
| } | |||
| .tableWrapper { | |||
| margin: 1.5rem 0; | |||
| overflow-x: auto; | |||
| } | |||
| &.resize-cursor { | |||
| cursor: ew-resize; | |||
| cursor: col-resize; | |||
| } | |||
| } | |||
| /* Input styles */ | |||
| /* .tiptap-input { | |||
| font-size: 14px; | |||
| @@ -14,6 +14,11 @@ import { Color } from '@tiptap/extension-color' | |||
| import ListItem from "@tiptap/extension-list-item"; | |||
| import TextStyle from "@tiptap/extension-text-style"; | |||
| import TextAlign from '@tiptap/extension-text-align' | |||
| import Table from '@tiptap/extension-table' | |||
| import TableCell from '@tiptap/extension-table-cell' | |||
| import TableHeader from '@tiptap/extension-table-header' | |||
| import TableRow from '@tiptap/extension-table-row' | |||
| import Gapcursor from '@tiptap/extension-gapcursor' | |||
| interface Props { | |||
| content?: string, | |||
| @@ -64,7 +69,14 @@ const MailField: React.FC<Props> = ({ | |||
| Highlight.configure({ multicolor: true }), | |||
| Color, | |||
| ListItem, | |||
| TabHandler | |||
| TabHandler, | |||
| Gapcursor, | |||
| Table.configure({ | |||
| resizable: true, | |||
| }), | |||
| TableRow, | |||
| TableHeader, | |||
| TableCell, | |||
| ], | |||
| content: content, | |||
| onUpdate({ editor }) { | |||
| @@ -73,15 +85,15 @@ const MailField: React.FC<Props> = ({ | |||
| } | |||
| console.log(editor.getHTML()) | |||
| }, | |||
| }) | |||
| }, []) | |||
| return ( | |||
| <Grid container rowSpacing={1}> | |||
| <Grid item xs={12}> | |||
| <MailToolbar editor={editor} /> | |||
| </Grid> | |||
| {/* <Grid item xs={12}> | |||
| {editor && <MailToolbar editor={editor} />} | |||
| </Grid> */} | |||
| <Grid item xs={12} > | |||
| <EditorContent className={error === true ? "tiptap-error" : ""} label="Template" editor={editor}/> | |||
| <EditorContent className={error === true ? "tiptap-error" : ""} label="Template" editor={editor} /> | |||
| </Grid> | |||
| </Grid> | |||
| ); | |||
| @@ -13,6 +13,8 @@ import FormatColorTextIcon from '@mui/icons-material/FormatColorText'; | |||
| import FormatAlignLeftIcon from '@mui/icons-material/FormatAlignLeft'; | |||
| import FormatAlignJustifyIcon from '@mui/icons-material/FormatAlignJustify'; | |||
| import FormatAlignRightIcon from '@mui/icons-material/FormatAlignRight'; | |||
| import { useTranslation } from "react-i18next"; | |||
| import GridOnIcon from '@mui/icons-material/GridOn'; | |||
| interface Props { | |||
| editor: Editor | null; | |||
| @@ -58,7 +60,7 @@ const fontFamily = [ | |||
| value: 'Georgia', | |||
| }, | |||
| { | |||
| } | |||
| ] | |||
| @@ -66,11 +68,12 @@ const MailToolbar: React.FC<Props> = ({ | |||
| editor | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| if (editor == null) { | |||
| return null | |||
| } | |||
| const [fontStyle, setFontStyle] = React.useState<string[]>(() => ["alignLeft"]); | |||
| const [fontStyle, setFontStyle] = React.useState<string[]>(["alignLeft"]); | |||
| const [colorHighlightValue, setColorHighlightValue] = React.useState<MuiColorInputValue>("red"); | |||
| const [colorTextValue, setColorTextValue] = React.useState<MuiColorInputValue>("black"); | |||
| const colorHighlightValueInputRef = React.useRef<any>(null); | |||
| @@ -189,6 +192,10 @@ const MailToolbar: React.FC<Props> = ({ | |||
| } | |||
| }, []) | |||
| const handleInsertTable = useCallback(() => { | |||
| editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run() | |||
| }, []) | |||
| React.useEffect(() => { | |||
| editor.on('selectionUpdate', ({ editor }) => { | |||
| const currentFormatList: string[] = [] | |||
| @@ -312,6 +319,47 @@ const MailToolbar: React.FC<Props> = ({ | |||
| <FormatAlignRightIcon /> | |||
| </ToggleButton> | |||
| </ToggleButtonGroup> | |||
| <ToggleButtonGroup | |||
| sx={{ marginTop: 2 }} | |||
| > | |||
| <ToggleButton id="insertTable" value="insertTable" onClick={() => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()}> | |||
| <GridOnIcon />{t("Insert Table")} | |||
| </ToggleButton> | |||
| <ToggleButton id="deleteTable" value="deleteTable" onClick={() => editor.chain().focus().deleteTable().run()}>{t("Delete table")}</ToggleButton> | |||
| <ToggleButton id="addColumnBefore" value="addColumnBefore" onClick={() => editor.chain().focus().addColumnBefore().run()}> | |||
| {t("Add column before")} | |||
| </ToggleButton> | |||
| <ToggleButton id="addColumnAfter" value="addColumnAfter" onClick={() => editor.chain().focus().addColumnAfter().run()}>{t("Add column after")}</ToggleButton> | |||
| <ToggleButton id="deleteColumn" value="deleteColumn" onClick={() => editor.chain().focus().deleteColumn().run()}>{t("Delete column")}</ToggleButton> | |||
| <ToggleButton id="addRowBefore" value="addRowBefore" onClick={() => editor.chain().focus().addRowBefore().run()}>{t("Add row before")}</ToggleButton> | |||
| <ToggleButton id="addRowAfter" value="addRowAfter" onClick={() => editor.chain().focus().addRowAfter().run()}>{t("Add row after")}</ToggleButton> | |||
| <ToggleButton id="deleteRow" value="deleteRow" onClick={() => editor.chain().focus().deleteRow().run()}>{t("Delete row")}</ToggleButton> | |||
| <ToggleButton id="mergeCells" value="mergeCells" onClick={() => editor.chain().focus().mergeCells().run()}>{t("Merge cells")}</ToggleButton> | |||
| <ToggleButton id="splitCell" value="splitCell" onClick={() => editor.chain().focus().splitCell().run()}>{t("Split cell")}</ToggleButton> | |||
| </ToggleButtonGroup> | |||
| <ToggleButtonGroup | |||
| // sx={{ marginLeft: 2 }} | |||
| > | |||
| <ToggleButton id="toggleHeaderColumn" value="toggleHeaderColumn" onClick={() => editor.chain().focus().toggleHeaderColumn().run()}> | |||
| {t("Toggle header column")} | |||
| </ToggleButton> | |||
| <ToggleButton id="toggleHeaderRow" value="toggleHeaderRow" onClick={() => editor.chain().focus().toggleHeaderRow().run()}> | |||
| {t("Toggle header row")} | |||
| </ToggleButton> | |||
| <ToggleButton id="toggleHeaderCell" value="toggleHeaderCell" onClick={() => editor.chain().focus().toggleHeaderCell().run()}> | |||
| {t("Toggle header cell")} | |||
| </ToggleButton> | |||
| <ToggleButton id="mergeOrSplit" value="mergeOrSplit" onClick={() => editor.chain().focus().mergeOrSplit().run()}>{t("Merge or split")}</ToggleButton> | |||
| <ToggleButton id="setCellAttribute" value="setCellAttribute" onClick={() => editor.chain().focus().setCellAttribute('colspan', 2).run()}> | |||
| {t("Set cell attribute")} | |||
| </ToggleButton> | |||
| <ToggleButton id="fixTables" value="fixTables" onClick={() => editor.chain().focus().fixTables().run()}>{t("Fix tables")}</ToggleButton> | |||
| <ToggleButton id="goToNextCell" value="goToNextCell" onClick={() => editor.chain().focus().goToNextCell().run()}>{t("Go to next cell")}</ToggleButton> | |||
| <ToggleButton id="goToPreviousCell" value="goToPreviousCell" onClick={() => editor.chain().focus().goToPreviousCell().run()}> | |||
| {t("Go to previous cell")} | |||
| </ToggleButton> | |||
| </ToggleButtonGroup> | |||
| </Grid> | |||
| ); | |||
| }; | |||
| @@ -5,7 +5,7 @@ import Close from "@mui/icons-material/Close"; | |||
| import Button from "@mui/material/Button"; | |||
| import Stack from "@mui/material/Stack"; | |||
| import { useRouter } from "next/navigation"; | |||
| import React, { useCallback, useState } from "react"; | |||
| import React, { useCallback, useContext, useEffect, useState } from "react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { | |||
| FieldErrors, | |||
| @@ -20,6 +20,10 @@ import { Error } from "@mui/icons-material"; | |||
| import { MailSave, saveMail, testEveryone, test7th, test15th, testSendMail } from "@/app/api/mail/actions"; | |||
| import SettingDetails from "./SettingDetails"; | |||
| import { errorDialog, submitDialog, successDialog } from "../Swal/CustomAlerts"; | |||
| import { MailTemplate } from "@/app/api/mail"; | |||
| import TemplateDetails from "./TemplateDetails"; | |||
| import QrCodeScanner from "../QrCodeScanner/QrCodeScanner"; | |||
| import { QcCodeScannerContext, useQcCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider"; | |||
| export interface Props { | |||
| defaultInputs?: MailSave, | |||
| @@ -34,10 +38,10 @@ const hasErrorsInTab = ( | |||
| return ( | |||
| errors.settings | |||
| ); | |||
| // case 1: | |||
| // return ( | |||
| // errors.template | |||
| // ); | |||
| case 1: | |||
| return ( | |||
| errors.templates | |||
| ); | |||
| default: | |||
| false; | |||
| } | |||
| @@ -134,6 +138,17 @@ const MailSetting: React.FC<Props> = ({ | |||
| const errors = formProps.formState.errors; | |||
| const scanner = useQcCodeScanner() | |||
| useEffect(() => { | |||
| scanner.startScan() | |||
| }, []) | |||
| console.log("test", scanner.values) | |||
| if (scanner.values.length > 3) { | |||
| console.log("test", scanner.values) | |||
| scanner.resetScan() | |||
| scanner.stopScan() | |||
| } | |||
| return ( | |||
| <FormProvider {...formProps}> | |||
| <Stack | |||
| @@ -161,17 +176,18 @@ const MailSetting: React.FC<Props> = ({ | |||
| } | |||
| iconPosition="end" | |||
| /> | |||
| {/* <Tab | |||
| label={t("Timesheet Template")} | |||
| <Tab | |||
| label={t("Template")} | |||
| icon={ | |||
| hasErrorsInTab(1, errors) ? ( | |||
| <Error sx={{ marginInlineEnd: 1 }} color="error" /> | |||
| ) : undefined | |||
| } | |||
| iconPosition="end" | |||
| /> */} | |||
| /> | |||
| </Tabs> | |||
| <SettingDetails isActive={tabIndex === 0}/> | |||
| <TemplateDetails isActive={tabIndex === 1} /> | |||
| {/* <TimesheetMailDetails isActive={tabIndex === 1} /> */} | |||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||
| @@ -1,7 +1,7 @@ | |||
| // import { fetchUserAbilities } from "@/app/utils/fetchUtil"; | |||
| import MailSetting from "./MailSetting"; | |||
| import MailSettingLoading from "./MailSettingLoading"; | |||
| import { fetchMailSetting } from "@/app/api/mail"; | |||
| import { fetchMailSetting, fetchMailTemplates } from "@/app/api/mail"; | |||
| interface SubComponents { | |||
| Loading: typeof MailSettingLoading; | |||
| @@ -11,10 +11,12 @@ const MailSettingWrapper: React.FC & SubComponents = async () => { | |||
| const [ | |||
| // abilities, | |||
| settings, | |||
| templates, | |||
| // timesheetTemplate, | |||
| ] = await Promise.all([ | |||
| // fetchUserAbilities(), | |||
| fetchMailSetting(), | |||
| fetchMailTemplates(), | |||
| // fetchMailTimesheetTemplate() | |||
| ]); | |||
| @@ -28,6 +30,7 @@ const MailSettingWrapper: React.FC & SubComponents = async () => { | |||
| <MailSetting | |||
| defaultInputs={{ | |||
| settings: settings, | |||
| templates: templates, | |||
| // template: tempTimesheetTemplate, | |||
| }} | |||
| /> | |||
| @@ -55,7 +55,7 @@ const SettingDetails: React.FC<Props> = ({ isActive }) => { | |||
| fields.map((field, index) => ( | |||
| <Grid container key={"row-" + index} justifyContent="flex-start" alignItems="center" spacing={2} columns={{ xs: 6, sm: 12 }} sx={{ mt: 1 }}> | |||
| <Grid item key={"col-1-" + index} xs={4}> | |||
| <Typography variant="body2">{t(field.name)}</Typography> | |||
| <Typography variant="body2">{`${t(field.name)}${requiredFields.some(name => field.name.toLowerCase().includes(name)) ? "*" : ""}`}</Typography> | |||
| </Grid> | |||
| { | |||
| field.name.toLowerCase().includes("password") === true ? | |||
| @@ -0,0 +1,239 @@ | |||
| "use client"; | |||
| import Stack from "@mui/material/Stack"; | |||
| import Box from "@mui/material/Box"; | |||
| import Card from "@mui/material/Card"; | |||
| import CardContent from "@mui/material/CardContent"; | |||
| import Grid from "@mui/material/Grid"; | |||
| import TextField from "@mui/material/TextField"; | |||
| import Typography from "@mui/material/Typography"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { Controller, useFieldArray, useFormContext } from "react-hook-form"; | |||
| import Link from "next/link"; | |||
| import React, { SyntheticEvent, useCallback, useMemo, useState } from "react"; | |||
| import MailField from "../MailField/MailField"; | |||
| import { MailSave } from "@/app/api/mail/actions"; | |||
| import { MailTemplate } from "@/app/api/mail"; | |||
| import { isEmpty, keys } from "lodash"; | |||
| import { Autocomplete, ListSubheader, MenuItem, UseAutocompleteProps } from "@mui/material"; | |||
| interface Props { | |||
| isActive: boolean; | |||
| } | |||
| interface OptionType { | |||
| value: number, | |||
| label: string, | |||
| group: string, | |||
| } | |||
| type fieldKeys = keyof MailTemplate | |||
| const TemplateDetails: React.FC<Props> = ({ isActive }) => { | |||
| const { t } = useTranslation(); | |||
| // const disabledFields: fieldKeys[] = [] | |||
| // const requiredFields: fieldKeys[] = [] | |||
| // const skipFields: fieldKeys[] = ["code", "subjectEng", "contentEng"] | |||
| const [selectedIndex, setSelectedIndex] = useState(0) | |||
| const { | |||
| register, | |||
| formState: { errors }, | |||
| control, | |||
| watch, | |||
| getValues | |||
| } = useFormContext<MailSave>(); | |||
| const { | |||
| fields, | |||
| } = useFieldArray({ | |||
| control, | |||
| name: "templates" | |||
| }) | |||
| const options: OptionType[] = useMemo(() => ( | |||
| fields.map((field, index) => { | |||
| return { | |||
| value: index, | |||
| label: `${field.code} - ${field.description}`, | |||
| group: field.type | |||
| } as OptionType | |||
| }) | |||
| ), [fields]) | |||
| const makeAutocompleteChangeHandler = useCallback(() => { | |||
| return (e: SyntheticEvent, newValue: OptionType) => { | |||
| setSelectedIndex(() => newValue.value); | |||
| }; | |||
| }, []); | |||
| // const renderFieldsBySwitchCases = useCallback(() => { | |||
| // const fieldNames = keys(fields[0]) as fieldKeys[]; | |||
| // return fieldNames | |||
| // .filter((name) => !skipFields.includes(name)) | |||
| // .map((name: fieldKeys, index: number) => { | |||
| // switch (name) { | |||
| // case "contentCht": | |||
| // return ( | |||
| // <Grid item xs={12}> | |||
| // <Controller | |||
| // control={control} | |||
| // name={`templates.${selectedIndex}.contentCht`} | |||
| // render={({ field }) => ( | |||
| // <MailField | |||
| // content={field.value} | |||
| // onChange={field.onChange} | |||
| // error={Boolean(errors.templates?.[selectedIndex]?.contentCht)} | |||
| // /> | |||
| // )} | |||
| // rules={{ | |||
| // required: requiredFields.includes(name), | |||
| // }} | |||
| // /> | |||
| // </Grid> | |||
| // ) | |||
| // default: | |||
| // return ( | |||
| // <Grid item xs={8}> | |||
| // <TextField | |||
| // label={t(name)} | |||
| // fullWidth | |||
| // {...register(`templates.${selectedIndex}.cc`), | |||
| // { | |||
| // required: requiredFields.includes(name) | |||
| // }} | |||
| // /> | |||
| // </Grid> | |||
| // ) | |||
| // } | |||
| // }) | |||
| // }, [fields]) | |||
| return ( | |||
| <Card sx={{ display: isActive ? "block" : "none" }}> | |||
| <CardContent component={Stack} spacing={4}> | |||
| <Box> | |||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||
| {t("Template")} | |||
| </Typography> | |||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||
| <Grid item xs={8}> | |||
| <Autocomplete | |||
| disableClearable | |||
| options={options} | |||
| value={options[selectedIndex]} | |||
| onChange={makeAutocompleteChangeHandler} | |||
| groupBy={(option) => ( | |||
| option.group && option.group.trim() !== '' ? option.group : 'Ungrouped' | |||
| )} | |||
| renderGroup={(params) => ( | |||
| <React.Fragment> | |||
| <ListSubheader>{params.group}</ListSubheader> | |||
| {params.children} | |||
| </React.Fragment> | |||
| )} | |||
| renderOption={( | |||
| params: React.HTMLAttributes<HTMLLIElement> & { key?: React.Key }, | |||
| option, | |||
| { selected }, | |||
| ) => { | |||
| // eslint-disable-next-line @typescript-eslint/no-unused-vars | |||
| const { key, ...rest } = params; | |||
| return ( | |||
| <MenuItem | |||
| {...rest} | |||
| disableRipple | |||
| value={option.value} | |||
| > | |||
| {option.label} | |||
| </MenuItem> | |||
| ); | |||
| }} | |||
| renderInput={(params) => <TextField {...params} variant="outlined" label={t("Select Template (View By Code - Description)")} />} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={8}> | |||
| <TextField | |||
| label={t("Code")} | |||
| fullWidth | |||
| {...register(`templates.${selectedIndex}.code`, { | |||
| required: "Code is required!" | |||
| })} | |||
| required | |||
| error={Boolean(errors.templates?.[selectedIndex]?.code)} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={8}> | |||
| <TextField | |||
| label={t("Description")} | |||
| fullWidth | |||
| {...register(`templates.${selectedIndex}.description`, { | |||
| required: "Description is required!" | |||
| })} | |||
| required | |||
| error={Boolean(errors.templates?.[selectedIndex]?.description)} | |||
| /> | |||
| </Grid> | |||
| {/* <Grid item xs={8}> | |||
| <TextField | |||
| label={t("To")} | |||
| fullWidth | |||
| {...register(`templates.${selectedIndex}.to`, { | |||
| required: "To is required!" | |||
| })} | |||
| required | |||
| error={Boolean(errors.templates?.[selectedIndex]?.to)} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={8}> | |||
| <TextField | |||
| label={t("Cc")} | |||
| fullWidth | |||
| {...register(`templates.${selectedIndex}.cc`)} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={8}> | |||
| <TextField | |||
| label={t("Required Params (split by ',')")} | |||
| fullWidth | |||
| {...register(`templates.${selectedIndex}.params`)} | |||
| /> | |||
| </Grid> */} | |||
| <Grid item xs={8}> | |||
| <TextField | |||
| label={t("Subject CHT")} | |||
| fullWidth | |||
| {...register(`templates.${selectedIndex}.subjectCht`, | |||
| { | |||
| required: "Mail Subject is required!", | |||
| })} | |||
| required | |||
| error={Boolean(errors.templates?.[selectedIndex]?.subjectCht)} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={12}> | |||
| <Controller | |||
| control={control} | |||
| name={`templates.${selectedIndex}.contentCht`} | |||
| render={({ field }) => ( | |||
| <MailField | |||
| content={field.value} | |||
| onChange={field.onChange} | |||
| error={Boolean(errors.templates?.[selectedIndex]?.contentCht)} | |||
| /> | |||
| )} | |||
| rules={{ | |||
| required: "Mail Content is required!", | |||
| }} | |||
| /> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </CardContent> | |||
| </Card> | |||
| ); | |||
| }; | |||
| export default TemplateDetails; | |||