diff --git a/Surge365.MassEmailReact.API/Controllers/TargetsController.cs b/Surge365.MassEmailReact.API/Controllers/TargetsController.cs index 31f2cbe..0d2e5fd 100644 --- a/Surge365.MassEmailReact.API/Controllers/TargetsController.cs +++ b/Surge365.MassEmailReact.API/Controllers/TargetsController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc; using Surge365.MassEmailReact.Application.DTOs; using Surge365.MassEmailReact.Application.Interfaces; using Surge365.MassEmailReact.Domain.Entities; +using Surge365.MassEmailReact.Infrastructure.Repositories; namespace Surge365.MassEmailReact.API.Controllers { @@ -62,5 +63,20 @@ namespace Surge365.MassEmailReact.API.Controllers return Ok(updatedTarget); } + + [HttpGet("{targetId}/sample")] + public async Task GetTargetSample(int targetId) + { + try + { + var sample = await _targetService.GetSampleData(targetId); + return Ok(sample); + } + catch (Exception ex) + { + // Log the exception (e.g., using ILogger if injected) + return StatusCode(500, new { message = "Error fetching sample data", error = ex.Message }); + } + } } } \ No newline at end of file diff --git a/Surge365.MassEmailReact.Application/Interfaces/ITargetRepository.cs b/Surge365.MassEmailReact.Application/Interfaces/ITargetRepository.cs index f0c09c2..df6a885 100644 --- a/Surge365.MassEmailReact.Application/Interfaces/ITargetRepository.cs +++ b/Surge365.MassEmailReact.Application/Interfaces/ITargetRepository.cs @@ -13,5 +13,6 @@ namespace Surge365.MassEmailReact.Application.Interfaces Task> GetAllAsync(bool activeOnly = true); Task CreateAsync(Target target); Task UpdateAsync(Target target); + Task GetSampleData(int targetId); } } diff --git a/Surge365.MassEmailReact.Application/Interfaces/ITargetService.cs b/Surge365.MassEmailReact.Application/Interfaces/ITargetService.cs index 2e9ee3b..dd79023 100644 --- a/Surge365.MassEmailReact.Application/Interfaces/ITargetService.cs +++ b/Surge365.MassEmailReact.Application/Interfaces/ITargetService.cs @@ -8,5 +8,6 @@ namespace Surge365.MassEmailReact.Application.Interfaces Task> GetAllAsync(bool activeOnly = true); Task CreateAsync(TargetUpdateDto targetDto); Task UpdateAsync(TargetUpdateDto targetDto); + Task GetSampleData(int targetId); } } diff --git a/Surge365.MassEmailReact.Domain/Entities/TargetSample.cs b/Surge365.MassEmailReact.Domain/Entities/TargetSample.cs new file mode 100644 index 0000000..6ab3a8d --- /dev/null +++ b/Surge365.MassEmailReact.Domain/Entities/TargetSample.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Surge365.MassEmailReact.Domain.Entities +{ + public class TargetSample + { + public List ColumnNames { get; set; } = new List(); + public List> Rows { get; set; } = new List>(); + } + +} \ No newline at end of file diff --git a/Surge365.MassEmailReact.Infrastructure/Repositories/TargetRepository.cs b/Surge365.MassEmailReact.Infrastructure/Repositories/TargetRepository.cs index af38f1c..9345d0b 100644 --- a/Surge365.MassEmailReact.Infrastructure/Repositories/TargetRepository.cs +++ b/Surge365.MassEmailReact.Infrastructure/Repositories/TargetRepository.cs @@ -118,5 +118,42 @@ namespace Surge365.MassEmailReact.Infrastructure.Repositories // Targets.Add(target); //} + public async Task GetSampleData(int targetId) + { + // Placeholder hardcoded sample data for testing + var sample = new TargetSample + { + ColumnNames = new List { "id", "name", "email", "age" }, + Rows = new List> + { + new Dictionary + { + { "id", "1" }, + { "name", "John Doe" }, + { "email", "john.doe@example.com" }, + { "age", "30" } + }, + new Dictionary + { + { "id", "2" }, + { "name", "Jane Smith" }, + { "email", "jane.smith@example.com" }, + { "age", "25" } + }, + new Dictionary + { + { "id", "3" }, + { "name", "Bob Johnson" }, + { "email", "bob.johnson@example.com" }, + { "age", "45" } + } + } + }; + + // Simulate async operation (e.g., DB call) for testing + await Task.Delay(100); // Optional: Mimics network latency + return sample; + } + } } diff --git a/Surge365.MassEmailReact.Infrastructure/Services/TargetService.cs b/Surge365.MassEmailReact.Infrastructure/Services/TargetService.cs index 9c375cd..7031f98 100644 --- a/Surge365.MassEmailReact.Infrastructure/Services/TargetService.cs +++ b/Surge365.MassEmailReact.Infrastructure/Services/TargetService.cs @@ -69,5 +69,9 @@ namespace Surge365.MassEmailReact.Infrastructure.Services return await _targetRepository.UpdateAsync(target); } + public async Task GetSampleData(int targetId) + { + return await _targetRepository.GetSampleData(targetId); + } } } diff --git a/Surge365.MassEmailReact.Web/src/components/modals/MailingEdit.tsx b/Surge365.MassEmailReact.Web/src/components/modals/MailingEdit.tsx index b5e4f48..d5fda6a 100644 --- a/Surge365.MassEmailReact.Web/src/components/modals/MailingEdit.tsx +++ b/Surge365.MassEmailReact.Web/src/components/modals/MailingEdit.tsx @@ -11,14 +11,22 @@ import { FormControlLabel, Box } from "@mui/material"; +import Template from "@/types/template"; import Mailing from "@/types/mailing"; +import Target from "@/types/target"; +import EmailList from "@/components/forms/EmailList"; +import TestEmailList from "@/types/testEmailList"; +import TemplateViewer from "@/components/modals/TemplateViewer" +import TargetSampleViewer from "@/components/modals/TargetSampleViewer" import { useSetupData, SetupData } from "@/context/SetupDataContext"; import { useForm, Controller, Resolver } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; -import EmailList from "@/components/forms/EmailList"; -import TestEmailList from "@/types/testEmailList"; -import { DatePicker } from '@mui/x-date-pickers/DatePicker'; + +import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import dayjs, { Dayjs } from 'dayjs'; // Import Dayjs for date handling type MailingEditProps = { open: boolean; @@ -47,6 +55,8 @@ const schema = yup.object().shape({ id: yup.number().nullable(), name: yup.string().required("Name is required") .test("unique-name", "Name must be unique", async function (value) { + if (value.length === 0) + return true; return await nameIsAvailable(this.parent.id, value); }), description: yup.string().default(""), @@ -59,7 +69,16 @@ const schema = yup.object().shape({ return setupData.targets.some(t => t.id === value); }), statusCode: yup.string().default("ED"), - scheduleDate: yup.date().nullable(), + scheduleDate: yup.date() + .nullable() + .when("$scheduleForLater", (scheduleForLater, schema) => { // Use context variable + return scheduleForLater + ? schema + .required("Schedule date is required when scheduled for later") + .min(new Date(), "Schedule date must be in the future") + : schema.nullable(); + }), + //scheduleDate: yup.date().nullable(), // .when("statusCode", { // is: (value: string) => value === "SC" || value === "SD", // String comparison @@ -104,8 +123,14 @@ const MailingEdit = ({ open, mailing, onClose, onSave }: MailingEditProps) => { const isNew = !mailing || mailing.id === 0; const setupData: SetupData = useSetupData(); const [approved, setApproved] = useState(false); + const [recurring, setRecurring] = useState(false); + const [scheduleForLater, setScheduleForLater] = useState(false); const [testEmailListId, setTestEmailListId] = useState(null); const [emails, setEmails] = useState([]); // State for email array + const [templateViewerOpen, setTemplateViewerOpen] = useState(false); + const [currentTemplate, setCurrentTemplate] = useState