feat!: первая готовая версия веб интерфейса
This commit is contained in:
@@ -243,13 +243,13 @@ public class ReportController(IReportAdapter adapter) : ControllerBase
|
||||
/// Отправка pdf отчета Вкладов и Кредитных программ по Валютам
|
||||
/// кладовщик pdf
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="mailInfo"></param>
|
||||
/// <param name="fromDate"></param>
|
||||
/// <param name="toDate"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> SendReportByCurrency(string email, DateTime fromDate, DateTime toDate, CancellationToken ct)
|
||||
public async Task<IActionResult> SendReportByCurrency([FromBody] DepositReportMailSendInfoBindingModel mailInfo, DateTime fromDate, DateTime toDate, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -267,9 +267,9 @@ public class ReportController(IReportAdapter adapter) : ControllerBase
|
||||
}
|
||||
|
||||
await _emailService.SendReportAsync(
|
||||
toEmail: email,
|
||||
subject: "Отчет по вкладам и кредитным программам по валютам",
|
||||
body: $"<h1>Отчет по вкладам и кредитным программам по валютам</h1><p>Отчет за период с {fromDate:dd.MM.yyyy} по {toDate:dd.MM.yyyy}</p>",
|
||||
toEmail: mailInfo.Email,
|
||||
subject: mailInfo.Subject,
|
||||
body: mailInfo.Body,
|
||||
attachmentPath: tempPathWithExtension
|
||||
);
|
||||
|
||||
@@ -333,16 +333,15 @@ public class ReportController(IReportAdapter adapter) : ControllerBase
|
||||
/// <summary>
|
||||
/// Отправка word отчета Вкладов по Кредитных программ
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="creditProgramIds"></param>
|
||||
/// <param name="mailInfo"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> SendReportDepositByCreditProgram(string email, [FromQuery] List<string>? creditProgramIds, CancellationToken ct)
|
||||
public async Task<IActionResult> SendReportDepositByCreditProgram([FromBody] CreditProgramReportMailSendInfoBindingModel mailInfo, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var report = await _adapter.CreateDocumentDepositByCreditProgramAsync(creditProgramIds, ct);
|
||||
var report = await _adapter.CreateDocumentDepositByCreditProgramAsync(mailInfo.CreditProgramIds, ct);
|
||||
var response = report.GetResponse(Request, Response);
|
||||
|
||||
if (response is FileStreamResult fileResult)
|
||||
@@ -356,9 +355,9 @@ public class ReportController(IReportAdapter adapter) : ControllerBase
|
||||
}
|
||||
|
||||
await _emailService.SendReportAsync(
|
||||
toEmail: email,
|
||||
subject: "Отчет по вкладам по кредитным программам",
|
||||
body: "<h1>Отчет по вкладам по кредитным программам</h1><p>В приложении находится отчет по вкладам по кредитным программам.</p>",
|
||||
toEmail: mailInfo.Email,
|
||||
subject: mailInfo.Subject,
|
||||
body: mailInfo.Body,
|
||||
attachmentPath: tempPathWithExtension
|
||||
);
|
||||
|
||||
@@ -378,16 +377,15 @@ public class ReportController(IReportAdapter adapter) : ControllerBase
|
||||
/// <summary>
|
||||
/// Отправка excel отчета Вкладов по Кредитных программ
|
||||
/// </summary>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="creditProgramIds"></param>
|
||||
/// <param name="mailInfo"></param>
|
||||
/// <param name="ct"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> SendExcelReportDepositByCreditProgram(string email, [FromQuery] List<string>? creditProgramIds, CancellationToken ct)
|
||||
public async Task<IActionResult> SendExcelReportDepositByCreditProgram([FromBody] CreditProgramReportMailSendInfoBindingModel mailInfo, CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
var report = await _adapter.CreateExcelDocumentDepositByCreditProgramAsync(creditProgramIds, ct);
|
||||
var report = await _adapter.CreateExcelDocumentDepositByCreditProgramAsync(mailInfo.CreditProgramIds, ct);
|
||||
var response = report.GetResponse(Request, Response);
|
||||
|
||||
if (response is FileStreamResult fileResult)
|
||||
@@ -401,9 +399,9 @@ public class ReportController(IReportAdapter adapter) : ControllerBase
|
||||
}
|
||||
|
||||
await _emailService.SendReportAsync(
|
||||
toEmail: email,
|
||||
subject: "Excel отчет по вкладам по кредитным программам",
|
||||
body: "<h1>Excel отчет по вкладам по кредитным программам</h1><p>В приложении находится Excel отчет по вкладам по кредитным программам.</p>",
|
||||
toEmail: mailInfo.Email,
|
||||
subject: mailInfo.Subject,
|
||||
body: mailInfo.Body,
|
||||
attachmentPath: tempPathWithExtension
|
||||
);
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { ConfigManager } from '@/lib/config';
|
||||
import type {
|
||||
CreditProgramReportMailSendInfoBindingModel,
|
||||
DepositReportMailSendInfoBindingModel,
|
||||
} from '@/types/types';
|
||||
|
||||
const API_URL = ConfigManager.loadUrl();
|
||||
|
||||
interface SendEmailRequest {
|
||||
toEmail: string;
|
||||
creditProgramIds?: string[];
|
||||
}
|
||||
|
||||
export const reportsApi = {
|
||||
// PDF отчеты
|
||||
getPdfReport: async (fromDate: string, toDate: string) => {
|
||||
@@ -26,7 +25,7 @@ export const reportsApi = {
|
||||
},
|
||||
|
||||
sendPdfReportByEmail: async (
|
||||
toEmail: string,
|
||||
mailInfo: DepositReportMailSendInfoBindingModel,
|
||||
fromDate: string,
|
||||
toDate: string,
|
||||
) => {
|
||||
@@ -38,9 +37,7 @@ export const reportsApi = {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
email: toEmail,
|
||||
}),
|
||||
body: JSON.stringify(mailInfo),
|
||||
},
|
||||
);
|
||||
if (!res.ok) {
|
||||
@@ -69,7 +66,9 @@ export const reportsApi = {
|
||||
return res.blob();
|
||||
},
|
||||
|
||||
sendWordReportByEmail: async (request: SendEmailRequest) => {
|
||||
sendWordReportByEmail: async (
|
||||
mailInfo: CreditProgramReportMailSendInfoBindingModel,
|
||||
) => {
|
||||
const res = await fetch(
|
||||
`${API_URL}/api/Report/SendReportDepositByCreditProgram`,
|
||||
{
|
||||
@@ -78,10 +77,7 @@ export const reportsApi = {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
email: request.toEmail,
|
||||
creditProgramIds: request.creditProgramIds,
|
||||
}),
|
||||
body: JSON.stringify(mailInfo),
|
||||
},
|
||||
);
|
||||
if (!res.ok) {
|
||||
@@ -109,7 +105,9 @@ export const reportsApi = {
|
||||
return res.blob();
|
||||
},
|
||||
|
||||
sendExcelReportByEmail: async (request: SendEmailRequest) => {
|
||||
sendExcelReportByEmail: async (
|
||||
mailInfo: CreditProgramReportMailSendInfoBindingModel,
|
||||
) => {
|
||||
const res = await fetch(
|
||||
`${API_URL}/api/Report/SendExcelReportDepositByCreditProgram`,
|
||||
{
|
||||
@@ -118,10 +116,7 @@ export const reportsApi = {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
email: request.toEmail,
|
||||
creditProgramIds: request.creditProgramIds,
|
||||
}),
|
||||
body: JSON.stringify(mailInfo),
|
||||
},
|
||||
);
|
||||
if (!res.ok) {
|
||||
|
||||
@@ -4,15 +4,11 @@ import { ReportViewer } from '../features/ReportViewer';
|
||||
import { reportsApi } from '@/api/reports';
|
||||
import { format } from 'date-fns';
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { EmailDialog } from '@/components/ui/EmailDialog';
|
||||
import type {
|
||||
CreditProgramReportMailSendInfoBindingModel,
|
||||
DepositReportMailSendInfoBindingModel,
|
||||
} from '@/types/types';
|
||||
|
||||
type ReportCategory = 'pdf' | 'word-excel' | null;
|
||||
type FileFormat = 'doc' | 'xls';
|
||||
@@ -26,7 +22,7 @@ export const Reports = (): React.JSX.Element => {
|
||||
mimeType: string;
|
||||
} | null>(null);
|
||||
const [isEmailDialogOpen, setIsEmailDialogOpen] = React.useState(false);
|
||||
const [email, setEmail] = React.useState('');
|
||||
const [isEmailLoading, setIsEmailLoading] = React.useState(false);
|
||||
const [pendingEmailAction, setPendingEmailAction] = React.useState<
|
||||
| {
|
||||
type: 'pdf';
|
||||
@@ -125,48 +121,58 @@ export const Reports = (): React.JSX.Element => {
|
||||
setIsEmailDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleEmailSubmit = async () => {
|
||||
if (!email.trim()) {
|
||||
toast.error('Пожалуйста, введите email');
|
||||
return;
|
||||
}
|
||||
|
||||
const handleEmailSubmit = async (emailData: {
|
||||
email: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
}) => {
|
||||
if (!pendingEmailAction) {
|
||||
toast.error('Нет данных для отправки');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsEmailLoading(true);
|
||||
try {
|
||||
if (pendingEmailAction.type === 'pdf') {
|
||||
const { fromDate, toDate } = pendingEmailAction.data;
|
||||
const mailInfo: DepositReportMailSendInfoBindingModel = {
|
||||
email: emailData.email,
|
||||
toEmail: emailData.email,
|
||||
subject: emailData.subject,
|
||||
body: emailData.body,
|
||||
fromDate: format(fromDate, 'yyyy-MM-dd'),
|
||||
toDate: format(toDate, 'yyyy-MM-dd'),
|
||||
};
|
||||
await reportsApi.sendPdfReportByEmail(
|
||||
email,
|
||||
mailInfo,
|
||||
format(fromDate, 'yyyy-MM-dd'),
|
||||
format(toDate, 'yyyy-MM-dd'),
|
||||
);
|
||||
} else {
|
||||
const { format: fileFormat, creditProgramIds } =
|
||||
pendingEmailAction.data;
|
||||
const mailInfo: CreditProgramReportMailSendInfoBindingModel = {
|
||||
email: emailData.email,
|
||||
toEmail: emailData.email,
|
||||
subject: emailData.subject,
|
||||
body: emailData.body,
|
||||
creditProgramIds,
|
||||
};
|
||||
if (fileFormat === 'doc') {
|
||||
await reportsApi.sendWordReportByEmail({
|
||||
toEmail: email,
|
||||
creditProgramIds,
|
||||
});
|
||||
await reportsApi.sendWordReportByEmail(mailInfo);
|
||||
} else {
|
||||
await reportsApi.sendExcelReportByEmail({
|
||||
toEmail: email,
|
||||
creditProgramIds,
|
||||
});
|
||||
await reportsApi.sendExcelReportByEmail(mailInfo);
|
||||
}
|
||||
}
|
||||
|
||||
toast.success('Отчет успешно отправлен на email');
|
||||
setIsEmailDialogOpen(false);
|
||||
setEmail('');
|
||||
setPendingEmailAction(null);
|
||||
} catch (error) {
|
||||
toast.error('Ошибка при отправке отчета на email');
|
||||
console.error(error);
|
||||
} finally {
|
||||
setIsEmailLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -197,38 +203,35 @@ export const Reports = (): React.JSX.Element => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Dialog open={isEmailDialogOpen} onOpenChange={setIsEmailDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Отправка отчета на email</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="email">Email адрес</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="example@example.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setIsEmailDialogOpen(false);
|
||||
setEmail('');
|
||||
setPendingEmailAction(null);
|
||||
}}
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button onClick={handleEmailSubmit}>Отправить</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<EmailDialog
|
||||
isOpen={isEmailDialogOpen}
|
||||
onClose={() => {
|
||||
setIsEmailDialogOpen(false);
|
||||
setPendingEmailAction(null);
|
||||
}}
|
||||
onSubmit={handleEmailSubmit}
|
||||
isLoading={isEmailLoading}
|
||||
defaultSubject={
|
||||
pendingEmailAction?.type === 'pdf'
|
||||
? 'Отчет по вкладам и кредитным программам по валютам'
|
||||
: pendingEmailAction?.data.format === 'doc'
|
||||
? 'Word отчет по вкладам по кредитным программам'
|
||||
: 'Excel отчет по вкладам по кредитным программам'
|
||||
}
|
||||
defaultBody={
|
||||
pendingEmailAction?.type === 'pdf'
|
||||
? `Отчет по вкладам и кредитным программам по валютам за период с ${
|
||||
pendingEmailAction.data.fromDate
|
||||
? format(pendingEmailAction.data.fromDate, 'dd.MM.yyyy')
|
||||
: ''
|
||||
} по ${
|
||||
pendingEmailAction.data.toDate
|
||||
? format(pendingEmailAction.data.toDate, 'dd.MM.yyyy')
|
||||
: ''
|
||||
}`
|
||||
: 'В приложении находится отчет по вкладам по кредитным программам.'
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
152
TheBank/bankui/src/components/ui/EmailDialog.tsx
Normal file
152
TheBank/bankui/src/components/ui/EmailDialog.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import React from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
|
||||
const emailSchema = z.object({
|
||||
email: z.string().email('Введите корректный email адрес'),
|
||||
subject: z.string().min(1, 'Тема письма обязательна'),
|
||||
body: z.string().min(1, 'Текст письма обязателен'),
|
||||
});
|
||||
|
||||
type EmailFormData = z.infer<typeof emailSchema>;
|
||||
|
||||
interface EmailDialogProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: (data: EmailFormData) => void;
|
||||
isLoading?: boolean;
|
||||
defaultSubject?: string;
|
||||
defaultBody?: string;
|
||||
}
|
||||
|
||||
export const EmailDialog: React.FC<EmailDialogProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSubmit,
|
||||
isLoading = false,
|
||||
defaultSubject = '',
|
||||
defaultBody = '',
|
||||
}) => {
|
||||
const form = useForm<EmailFormData>({
|
||||
resolver: zodResolver(emailSchema),
|
||||
defaultValues: {
|
||||
email: '',
|
||||
subject: defaultSubject,
|
||||
body: defaultBody,
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isOpen) {
|
||||
form.reset({
|
||||
email: '',
|
||||
subject: defaultSubject,
|
||||
body: defaultBody,
|
||||
});
|
||||
}
|
||||
}, [isOpen, defaultSubject, defaultBody, form]);
|
||||
|
||||
const handleSubmit = (data: EmailFormData) => {
|
||||
onSubmit(data);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
form.reset();
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Отправка отчета на почту</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(handleSubmit)}
|
||||
className="space-y-4"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email адрес</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="example@example.com"
|
||||
type="email"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="subject"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Тема письма</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Тема письма" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="body"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Текст письма</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder="Текст письма..."
|
||||
className="min-h-[100px]"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={handleClose}
|
||||
disabled={isLoading}
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button type="submit" disabled={isLoading}>
|
||||
{isLoading ? 'Отправка...' : 'Отправить'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
22
TheBank/bankui/src/components/ui/textarea.tsx
Normal file
22
TheBank/bankui/src/components/ui/textarea.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Textarea = React.forwardRef<
|
||||
HTMLTextAreaElement,
|
||||
React.TextareaHTMLAttributes<HTMLTextAreaElement>
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
Textarea.displayName = 'Textarea';
|
||||
|
||||
export { Textarea };
|
||||
@@ -108,3 +108,21 @@ export interface MailSendInfoBindingModel {
|
||||
body: string;
|
||||
attachmentPath?: string;
|
||||
}
|
||||
|
||||
export interface ReportMailSendInfoBindingModel
|
||||
extends MailSendInfoBindingModel {
|
||||
email: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface CreditProgramReportMailSendInfoBindingModel
|
||||
extends ReportMailSendInfoBindingModel {
|
||||
creditProgramIds: string[];
|
||||
}
|
||||
|
||||
export interface DepositReportMailSendInfoBindingModel
|
||||
extends ReportMailSendInfoBindingModel {
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user