From 680ef3a7e21c9b5550784ff738279204875c2cf9 Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Fri, 14 Nov 2025 15:20:46 +0330 Subject: [PATCH] Implement Chromium-based PDF generation service; add fetchAndDownloadPdf utility and update contract generation endpoint --- src/FrontOffice.Main/ConfigureServices.cs | 4 +- src/FrontOffice.Main/FrontOffice.Main.csproj | 6 +- .../Pages/RegisterWizard.razor | 4 +- .../Pages/RegisterWizard.razor.cs | 94 ++++++++--- src/FrontOffice.Main/Pages/_Host.cshtml | 26 ++- src/FrontOffice.Main/Program.cs | 42 ++--- .../Utilities/MobileNumberEncryptor.cs | 34 ++-- .../Utilities/Pdf/ChromiumPdfService.cs | 148 ++++++++++++++++++ .../Utilities/Pdf/HtmlToPdfService.cs | 36 +---- 9 files changed, 297 insertions(+), 97 deletions(-) create mode 100644 src/FrontOffice.Main/Utilities/Pdf/ChromiumPdfService.cs diff --git a/src/FrontOffice.Main/ConfigureServices.cs b/src/FrontOffice.Main/ConfigureServices.cs index 13cfe5b..903ea12 100644 --- a/src/FrontOffice.Main/ConfigureServices.cs +++ b/src/FrontOffice.Main/ConfigureServices.cs @@ -38,8 +38,8 @@ public static class ConfigureServices services.AddScoped(); // Device detection: very light, dependency-free services.AddScoped(); - // PDF generation - services.AddSingleton(); + // PDF generation (Chromium only) + services.AddSingleton(); return services; } diff --git a/src/FrontOffice.Main/FrontOffice.Main.csproj b/src/FrontOffice.Main/FrontOffice.Main.csproj index 4655804..672baf7 100644 --- a/src/FrontOffice.Main/FrontOffice.Main.csproj +++ b/src/FrontOffice.Main/FrontOffice.Main.csproj @@ -19,14 +19,12 @@ - - - + + - diff --git a/src/FrontOffice.Main/Pages/RegisterWizard.razor b/src/FrontOffice.Main/Pages/RegisterWizard.razor index 5dfb0bd..566fce8 100644 --- a/src/FrontOffice.Main/Pages/RegisterWizard.razor +++ b/src/FrontOffice.Main/Pages/RegisterWizard.razor @@ -112,12 +112,12 @@ OnClick="DownloadContract"> دانلود/پرینت قرارداد نمونه - فرمت: PDF از طریق Print + فرمت: PDF + Label="قوانین و مقررات را مطالعه کرده‌ام و می‌پذیرم" /> diff --git a/src/FrontOffice.Main/Pages/RegisterWizard.razor.cs b/src/FrontOffice.Main/Pages/RegisterWizard.razor.cs index 711a9d8..8c3055d 100644 --- a/src/FrontOffice.Main/Pages/RegisterWizard.razor.cs +++ b/src/FrontOffice.Main/Pages/RegisterWizard.razor.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using DateTimeConverterCL; using FrontOffice.BFF.User.Protobuf.Protos.User; using FrontOffice.Main.Shared; using FrontOffice.Main.Utilities; @@ -19,8 +20,8 @@ public partial class RegisterWizard private int _activeStep; private bool _isSubmitting; private bool _completed; - private AuthDialog _authDialog; - private MudStepper _mudStepper; + private AuthDialog? _authDialog; + private MudStepper? _mudStepper; private UpdateUserRequest _updateUserRequest = new(); private bool _isAuthenticated; @@ -49,17 +50,20 @@ public partial class RegisterWizard try { var existUser = await UserContract.GetUserAsync(new Empty()); - if (existUser != null && !string.IsNullOrEmpty(existUser.FirstName) && string.IsNullOrWhiteSpace(_model.FirstName)) + if (existUser != null && !string.IsNullOrEmpty(existUser.FirstName) && + string.IsNullOrWhiteSpace(_model.FirstName)) { _model.FirstName = existUser.FirstName; } - if (existUser != null && !string.IsNullOrEmpty(existUser.LastName) && string.IsNullOrWhiteSpace(_model.LastName)) + if (existUser != null && !string.IsNullOrEmpty(existUser.LastName) && + string.IsNullOrWhiteSpace(_model.LastName)) { _model.LastName = existUser.LastName; } - if (existUser != null && !string.IsNullOrEmpty(existUser.NationalCode) && string.IsNullOrWhiteSpace(_model.NationalCode)) + if (existUser != null && !string.IsNullOrEmpty(existUser.NationalCode) && + string.IsNullOrWhiteSpace(_model.NationalCode)) { _model.NationalCode = existUser.NationalCode; } @@ -68,9 +72,10 @@ public partial class RegisterWizard { Console.WriteLine(e); } + await InvokeAsync(StateHasChanged); } - + await base.OnAfterRenderAsync(firstRender); } @@ -152,17 +157,64 @@ public partial class RegisterWizard private async void DownloadContract() { - // Example dynamic HTML (could be constructed based on user data) - var html = $"

قرارداد اولیه کاربر

نام: {_model.FirstName ?? "-"} {_model.LastName ?? "-"}

کد ملی: {_model.NationalCode ?? "-"}

این یک نسخه نمونه است.

"; +//var html = $"

قرارداد اولیه کاربر

نام: {_model.FirstName ?? "-"} {_model.LastName ?? "-"}

کد ملی: {_model.NationalCode ?? "-"}

این یک نسخه نمونه است.

"; + var html = + $"" + + "قرارداد تعهد و سلب مسئولیت کارابازار

قرارداد تعهد و سلب مسئولیت

بین کارابازار و کاربر

" + + "

این قرارداد تعهد و سلب مسئولیت («قرارداد») در تاریخ " + + $"{DateTime.Now.MiladiToJalali()} بین:

" + + "1. شرکت/مجموعه کارابازار (که از این پس «کارابازار» نامیده می‌شود)،

2. شخص حقیقی زیر (که از این پس «کاربر» نامیده می‌شود):

" + + $"

نام و نام خانوادگی کاربر: {_model.FirstName } {_model.LastName}

" + + $"

شماره ملی: {_model.NationalCode?.PersianToEnglish()}

" + + $"

شماره تماس: {_model.NationalCode?.PersianToEnglish() }

" + + "

کارابازار و کاربر که در این قرارداد جداگانه «طرف» و مجموعاً «طرفین» نامیده می‌شوند، با علم و آگاهی کامل، مفاد زیر را پذیرفته و امضا می‌نمایند.

ماده ۱ - موضوع قرارداد

" + + "

موضوع این قرارداد، تعیین حدود و چارچوب تعهدات و سلب مسئولیت کارابازار در قبال استفاده کاربر ازخدمات، باشگاه مشتریان، مزایا، طرح‌های همکاری، شبکه معرفی، خرید از فروشگاه‌های طرف‌قراردادو هرگونه امکانات و تسهیلات مرتبط است که حسب مورد توسط کارابازار یا از طریق پلتفرم‌ها و شرکت‌هایهمکار در اختیار کاربر قرار می‌گیرد، بدون اینکه جزئیات مدل کسب‌وکار، شیوه تأمین منابع مالی،شبکه همکاری یا ساختار داخلی آن در این قرارداد ذکر یا افشا شود.

" + + "

ماده ۲ - اقرار و آگاهی کاربر

۲-۱. کاربر اقرار می‌نماید که کلیه اطلاعات کلی لازم در خصوص نحوه فعالیت، مزایا، ریسک‌های احتمالی," + + "سازوکار کلی استفاده از خدمات، و چارچوب کلی همکاری را از مراجع رسمی معرفی‌شده توسط کارابازاردریافت کرده، سؤالات خود را مطرح نموده و پاسخ مناسب را دریافت نموده است.

۲-۲. کاربر با امضای این قرارداد اقرار می‌نماید که:

  • با اراده آزاد و بدون هرگونه اجبار، اکراه، فریب یا سوءاستفاده از ناآگاهی وارد این همکاری شده است.
  • از احتمال وجود ریسک‌های مالی، اعتباری، قانونی و شبکه‌ای مرتبط با فعالیت خود آگاه بوده و آن را می‌پذیرد.
  • " + + "
  • هرگونه تصمیم برای استفاده از تسهیلات، مزایا، خدمات باشگاه مشتریان، فعالیت در شبکه معرفی، خرید از فروشگاه‌ها و جذب افراد جدید، تصمیمی شخصی و آگاهانه است و به‌عنوان تعهد یا ضمانت بازده مشخص از سوی کارابازار تلقی نمی‌شود.
" + + "

ماده ۳ - سلب مسئولیت کارابازار

۳-۱. کاربر صراحتاً می‌پذیرد که کارابازار صرفاً در چارچوب قوانین و مقررات حاکم و مطابق قراردادها و تفاهم‌نامه‌های خود با شرکای تجاری و پلتفرم‌های همکار، خدمات و امکانات مشخصی را در اختیار کاربر قرار می‌دهد و هیچ‌گونه تعهدی نسبت به:

  • تحقق سود یا بازده مالی مشخص،
  • ثبات ارزش هرگونه اعتبار، امتیاز، یا منافع غیرنقدی که ممکن است در قالب طرح‌ها به کاربر تخصیص یابد،
  • " + + "
  • عملکرد اشخاص ثالث (از جمله پلتفرم‌ها، فروشگاه‌ها، شرکت‌های بیمه، ارائه‌دهندگان خدمات سلامت و هر شخص حقیقی یا حقوقی همکار)،
  • تصمیمات و اقدامات کاربر در جذب افراد جدید و ایجاد زیرمجموعه،

نداشته و نخواهد داشت مگر در حدودی که به‌صراحت در قرارداد یا قوانین آمره بر عهده کارابازار گذاشته شده است.

" + + "۳-۲. هرگونه خسارت، دعوی، تهدید، مطالبه، ادعا، شکایت یا پیگیری حقوقی که ناشی از تصمیمات شخصی کاربر،نحوه استفاده وی از تسهیلات و مزایا، جذب دیگران، خرید کالا و خدمات، یا تعاملات او با اشخاص ثالث باشد،مستقیماً متوجه خود کاربر است و کارابازار نسبت به آن هیچ‌گونه مسئولیت مدنی، کیفری، مالی یا قراردادی ندارد," + + "مگر در مواردی که طبق قوانین آمره، مسئولیت صریح و غیرقابل‌اسقاط بر عهده کارابازار قرار گرفته باشد.

۳-۳. کاربر می‌پذیرد که هیچ‌گونه ادعایی مبنی بر فریب، اغفال، ارائه اطلاعات نادرست یا وعده‌های خارج ازچهارچوب رسمی از سوی کارابازار یا نمایندگان رسمی آن، پس از امضای این قرارداد، از وی مسموع نخواهد بود،مگر آنکه طبق رأی قطعی مرجع قضایی صالح، خلاف آن ثابت شود.

ماده ۴ - مسئولیت کاربر در قبال تهدید، ادعا و شکایت

" + + "

۴-۱. کاربر متعهد است در صورت طرح هرگونه تهدید، ادعا، مطالبه، شکایت یا پیگیری حقوقی از سوی اشخاص ثالث(از جمله افراد جذب‌شده توسط کاربر، زیرمجموعه‌ها، خریداران معرفی‌شده یا سایر اشخاص)، که ناشی از عملکردشخصی کاربر، معرفی‌های وی، شیوه تبلیغ، وعده‌های خارج از چارچوب رسمی کارابازار، یا نقض قوانین و مقرراتباشد، خود رأساً پاسخگو باشد و کارابازار را از هرگونه مسئولیت و هزینه احتمالی در این خصوص مصون بدارد.

۴-۲. کاربر می‌پذیرد که:

  • هرگونه تهدید یا ادعای بی‌جا، غیرمنطقی، غیرمستند یا مغایر با مفاد این قرارداد، متوجه شخص کاربر است.
  • " + + "
  • در صورت وارد آمدن خسارت مادی یا معنوی به کارابازار ناشی از چنین تهدیدات یا ادعاهایی، کارابازار حق دارد نسبت به مطالبه خسارت از کاربر، طبق قوانین و از طریق مراجع صالح اقدام نماید.
" + + "

ماده ۵ - محرمانگی و عدم افشای جزئیات کسب‌وکار

۵-۱. کاربر می‌پذیرد که جزئیات فنی، مالی، ساختار شبکه، شیوه توزیع مزایا، قراردادهای داخلی کارابازار باشرکای تجاری و پلتفرم‌های همکار، اطلاعات محرمانه تجاری محسوب شده و خارج از این قرارداد است و افشای آن‌هاتوسط کاربر بدون مجوز کتبی کارابازار، مجاز نمی‌باشد.

۵-۲. کاربر حق استناد به عدم درج جزئیات ساختار کسب‌وکار در این قرارداد را به‌عنوان دلیل بطلان، فسخ یاادعای فریب علیه کارابازار از خود سلب می‌نماید.

" + + "

ماده ۶ - کد تأیید یکبارمصرف (OTP) و شناسه قرارداد

۶-۱. به منظور تأیید هویت کاربر و جایگزین کردن امضا در محیط غیرحضوری، برای این قرارداد یک شناسه یکتا(GUID) تولید شده و از طریق سامانه کارابازار، کد تأیید یکبارمصرف (OTP) مرتبط با آن برای کاربر ارسالمی‌گردد.

" + + "

۶-۲. کاربر با وارد کردن صحیح کد تأیید (OTP) در سامانه، اقرار می‌نماید که:

  • مالک شماره تماس ثبت‌شده در این قرارداد است یا با اجازه قانونی مالک از آن استفاده می‌کند،
  • از محتوای قرارداد به‌طور کامل آگاه است و آن را بدون ابهام و با رضایت کامل می‌پذیرد،
  • شناسه یکتا و OTP ارسال‌شده، معادل امضای الکترونیکی معتبر وی بر روی این قرارداد است.
" + + "

ماده ۷ - مدت، اصلاح و حاکمیت قانون

" + + "

۷-۱. این قرارداد از تاریخ تأیید نهایی (ثبت OTP) نافذ و لازم‌الاجرا بوده و تا زمانی که همکاری کاربر باکارابازار ادامه دارد، معتبر خواهد بود؛ مگر آنکه مطابق قوانین یا توافق طرفین، خاتمه یابد.

۷-۲. هرگونه اصلاح در مفاد این قرارداد صرفاً از طریق اطلاع‌رسانی رسمی کارابازار و پذیرش کاربر (اعم ازامضای مکتوب، تأیید در سامانه، یا سایر روش‌های الکترونیکی مورد تأیید کارابازار) معتبر خواهد بود.

۷-۳. این قرارداد از هر حیث، تابع قوانین و مقررات حاکم بر جمهوری اسلامی ایران بوده و در صورت بروز اختلاف،مراجع صالح قضایی محل استقرار کارابازار، صلاحیت رسیدگی خواهند داشت؛ مگر آنکه به نحو دیگری بین طرفین توافقشود." + + "

ماده ۸ - اقرار نهایی کاربر

کاربر با امضای این قرارداد (از طریق ثبت کد تأیید یکبارمصرف OTP متناظر با شناسه یکتا)، صریحاً اعلام می‌نماید که:" + + "

  • کلیه مفاد قرارداد را به‌دقت مطالعه نموده و آن را درک کرده است،
  • " + + "
  • پرسش‌های خود را مطرح نموده و پاسخ روشن دریافت کرده است،
  • ریسک‌ها، محدودیت‌ها و حدود مسئولیت کارابازار را می‌پذیرد،
  • حق هرگونه ادعای مغایر با مفاد این قرارداد، به‌ویژه ادعاهای بی‌اساس، غیرمنطقی یا ناشی از برداشت نادرست شخصی را از خود سلب و اسقاط می‌نماید؛ مگر در حدودی که قانون به‌طور آمره اجازه سلب آن را نداده باشد.

شناسه یکتای قرارداد :

"+Guid.NewGuid()+"

"; - // Option A: trigger browser print dialog for PDF (client-side) try { - await Js.InvokeVoidAsync("printUtils.printHtml", html, new { title = "قرارداد نمونه" }); +// If HTML is short use GET to simplify (URL length safe) + if (html.Length < 1800) + { + var encoded = Uri.EscapeDataString(html); + Navigation.NavigateTo($"contract/generate?html={encoded}&fileName=sample-contract", true); + return; + } + +// For larger HTML use POST via JS fetch to stream file + var fileName = "sample-contract"; + await Js.InvokeVoidAsync("fetchAndDownloadPdf", html, fileName); } - catch + catch (Exception ex) { - // Fallback to server-side generation endpoint if JS fails + Console.WriteLine($"PDF download error: {ex.Message}"); +// Fallback to GET (may truncate if too long) var encoded = Uri.EscapeDataString(html); Navigation.NavigateTo($"contract/generate?html={encoded}&fileName=sample-contract", true); } @@ -170,9 +222,9 @@ public partial class RegisterWizard private void OnPhoneVerified() { - // Move to next step after phone verification success - // _activeStep = 1; - //StateHasChanged(); +// Move to next step after phone verification success +// _activeStep = 1; +//StateHasChanged(); } private async Task SavePersonalInfo() @@ -185,12 +237,12 @@ public partial class RegisterWizard try { - // _updateUserRequest.AvatarPath="test"; - // _updateUserRequest.BirthDate=Timestamp.FromDateTime(DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc)); - // _updateUserRequest.EmailNotifications = true; - // _updateUserRequest.PushNotifications = true; - // _updateUserRequest.SmsNotifications = true; - // +// _updateUserRequest.AvatarPath="test"; +// _updateUserRequest.BirthDate=Timestamp.FromDateTime(DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc)); +// _updateUserRequest.EmailNotifications = true; +// _updateUserRequest.PushNotifications = true; +// _updateUserRequest.SmsNotifications = true; +// _updateUserRequest.FirstName = _model.FirstName; _updateUserRequest.LastName = _model.LastName; _updateUserRequest.NationalCode = _model.NationalCode.PersianToEnglish(); diff --git a/src/FrontOffice.Main/Pages/_Host.cshtml b/src/FrontOffice.Main/Pages/_Host.cshtml index eb3599d..fcd16a8 100644 --- a/src/FrontOffice.Main/Pages/_Host.cshtml +++ b/src/FrontOffice.Main/Pages/_Host.cshtml @@ -35,7 +35,31 @@ - +