PageProjetosFileStream.razor 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. @page "/pageprojetoFileStream/{projetoCodigo:guid}"
  2. @attribute [Authorize]
  3. @rendermode InteractiveServer
  4. @using System.ComponentModel.DataAnnotations
  5. @using System.Text
  6. @using System.Text.Encodings.Web
  7. @using ConcursoProjetos.Service
  8. @using Microsoft.AspNetCore.Identity
  9. @using Microsoft.AspNetCore.WebUtilities
  10. @using System;
  11. @using System.IO;
  12. @inject UserManager<ApplicationUser> UserManager
  13. @inject AuthenticationStateProvider AuthenticationStateProvider
  14. @inject ICandidatoService candidatoService
  15. @inject IProjetoService projetoService
  16. @inject IDocumentoService documentoService
  17. @inject ITipoDocumentoService tipoDocumentoServico
  18. @inject DialogService dialogService
  19. @inject NotificationService notificationService
  20. @inject NavigationManager Navigation
  21. <PageTitle>Cadastro de Projeto</PageTitle>
  22. <div class="container mt-5">
  23. <h3>Cadastro de Projeto</h3>
  24. <br />
  25. <RadzenTemplateForm TItem="InputModel" Data=@Input Submit=@OnSubmit InvalidSubmit=@OnInvalidSubmit>
  26. <RadzenRow Gap="1rem">
  27. <RadzenColumn Size="12" SizeSM="6">
  28. <RadzenStack>
  29. <RadzenFormField Text="Nome do Projeto" Variant="@variant">
  30. <RadzenTextBox Name="Nome" @bind-Value="@Input.Nome" />
  31. <RadzenRequiredValidator Component="Nome" Text="Nome do projeto é obrigatório" Popup=@popup Style="position: relative" />
  32. </RadzenFormField>
  33. <RadzenFormField Text="Nome do Responsável Técnico da Empresa" Variant="@variant">
  34. <RadzenTextBox Name="ResponsavelNome" @bind-Value="@Input.ResponsavelTecnicoNomeCompleto" />
  35. <RadzenRequiredValidator Component="ResponsavelNome" Text="Nome do responsável técnico é obrigatório" Popup=@popup Style="position: relative" />
  36. </RadzenFormField>
  37. <RadzenFormField Text="CPF do Responsável Técnico da Empresa" Variant="@variant">
  38. <RadzenMask Mask="***.***.***-**" CharacterPattern="[0-9]" Placeholder="000.000.000-00" Name="ResponsavelTecnicoCpf" @bind-Value="Input.ResponsavelTecnicoCpf" />
  39. <RadzenRequiredValidator Component="ResponsavelTecnicoCpf" Text="Cpf do responsável técnico é obrigatório" Popup=@popup Style="position: relative" />
  40. </RadzenFormField>
  41. </RadzenStack>
  42. </RadzenColumn>
  43. <RadzenColumn Size="12" SizeSM="6">
  44. <RadzenStack>
  45. <RadzenFormField Text="Número de Unidades Habitacionais por Pavimento" Variant="@variant">
  46. <RadzenDropDown @bind-Value="@Input.NumeroUnidades" Data="@listaDeUnidades" />
  47. </RadzenFormField>
  48. <RadzenFormField Text="E-mail do Responsável Técnico da Empresa" Variant="@variant">
  49. <RadzenTextBox Name="Email" @bind-Value="@Input.ResponsavelTecnicoEmail" />
  50. <RadzenRequiredValidator Component="Email" Text="E-mail é obrigatório" Popup=@popup Style="position: relative" />
  51. <RadzenEmailValidator Component="Email" Text="Informe um e-mail válido" Popup=@popup Style="position: relative" />
  52. </RadzenFormField>
  53. <RadzenFormField Text="Telefone do Responsável Técnico da Empresa" Variant="@variant">
  54. <RadzenMask Mask="(**) *****-****" CharacterPattern="[0-9]" Placeholder="(00) 00000-0000" Name="ResponsavelTecnicoTelefone" @bind-Value="Input.ResponsavelTecnicoTelefone" />
  55. <RadzenRequiredValidator Component="ResponsavelTecnicoTelefone" Text="Telefone do responsável técnico é obrigatório" Popup=@popup Style="position: relative" />
  56. </RadzenFormField>
  57. </RadzenStack>
  58. </RadzenColumn>
  59. </RadzenRow>
  60. <RadzenRow AlignItems="AlignItems.Center" class="rz-mt-4">
  61. <RadzenColumn Size="12" Offset="0" SizeMD="8" OffsetMD="4">
  62. <RadzenButton ButtonType="ButtonType.Submit" Text="@rotuloBotaoGravar"></RadzenButton>
  63. </RadzenColumn>
  64. </RadzenRow>
  65. </RadzenTemplateForm>
  66. <!-- Seção para envio de anexos -->
  67. @if (_projeto != null && _candidato != null && _documentosModel != null)
  68. {
  69. <h4 class="mt-4">Envio de Anexos</h4>
  70. <div class="table-responsive">
  71. <table class="table table-bordered">
  72. <thead>
  73. <tr>
  74. <th>Tipo de Arquivo</th>
  75. <th>Escolher o Arquivo</th>
  76. <th>Status</th>
  77. </tr>
  78. </thead>
  79. <tbody>
  80. @foreach (var model in _documentosModel)
  81. {
  82. <tr>
  83. <td><strong>@model.TipoDocumento.Nome</strong></td>
  84. <td>
  85. <InputFile disabled="@UploadingLargeFile" OnChange="@((args) => OnLargeFileInputFileChange(args, _candidato, _projeto, model))" />
  86. @if ((listaTipoDocumentoLoading.Count > 0) && listaTipoDocumentoLoading.Where(x => x.TipoDocumento.Id == model.TipoDocumento.Id).First().Uploading)
  87. {
  88. <progress style="height:50px;width:100%;" value="@UploadedBytes" max="@TotalBytes"></progress>
  89. }
  90. </td>
  91. <td>
  92. @if (model.Status)
  93. {
  94. <i class="bi bi-check-all fs-5 text-success"></i>
  95. }
  96. else
  97. {
  98. <i class="bi bi-x fs-5 text-danger"></i>
  99. }
  100. </td>
  101. </tr>
  102. }
  103. </tbody>
  104. </table>
  105. </div>
  106. }
  107. </div>
  108. @code {
  109. bool popup;
  110. Variant variant = Variant.Outlined;
  111. bool Uploading = false;
  112. bool UploadingLargeFile = false;
  113. string LargeUploadMessage = "";
  114. long UploadedBytes;
  115. long TotalBytes;
  116. async Task OnLargeFileInputFileChange(InputFileChangeEventArgs args, Candidato candidato, Projeto projeto, InputModelDocuments modelDocument)
  117. {
  118. Documento resultado;
  119. // UploadedBytes = 0;
  120. // Disable the file input field
  121. //UploadingLargeFile = true;
  122. var tipoDocumentoLoading = listaTipoDocumentoLoading.Where(x => x.TipoDocumento.Id == modelDocument.TipoDocumento.Id).First();
  123. tipoDocumentoLoading.Uploading = true;
  124. await InvokeAsync(StateHasChanged);
  125. // calculate the chunks we have to send
  126. TotalBytes = args.File.Size;
  127. long percent = 0;
  128. long chunkSize = 1000000;
  129. long numChunks = TotalBytes / chunkSize;
  130. long remainder = TotalBytes % chunkSize;
  131. // get new filename with a bit of entropy
  132. string justFileName = Path.GetFileNameWithoutExtension(args.File.Name);
  133. string extension = Path.GetExtension(args.File.Name);
  134. string newFileNameWithoutPath = $"Projeto_{projeto.Id}_{modelDocument.TipoDocumento.Nome}-{justFileName}-{DateTime.Now.Ticks.ToString()}{extension}";
  135. //string projectPath = $"{Environment.CurrentDirectory}\\files\\{projeto.Id}";
  136. string projectPath = projetoService.Path(projeto.Id);
  137. string filename = $"{projectPath}\\{newFileNameWithoutPath}";
  138. if (!Directory.Exists(projectPath))
  139. {
  140. Directory.CreateDirectory(projectPath);
  141. GerarArquivoTextoParaProjeto(candidato, projeto, projectPath);
  142. }
  143. // Delete the file if it already exists in our \Files folder
  144. if (File.Exists(filename))
  145. {
  146. File.Delete(filename);
  147. }
  148. // Open the input and output file streams
  149. // using (var inStream = args.File.OpenReadStream(long.MaxValue))
  150. // {
  151. // using (var outStream = File.OpenWrite(filename))
  152. // {
  153. // while (UploadedBytes < TotalBytes)
  154. // {
  155. // var whatsLeft = TotalBytes - UploadedBytes;
  156. // if (whatsLeft < chunkSize)
  157. // chunkSize = remainder;
  158. // // Read the next chunk
  159. // var bytes = new byte[chunkSize];
  160. // var buffer = new Memory<byte>(bytes);
  161. // var read = await inStream.ReadAsync(buffer);
  162. // // Write it
  163. // await outStream.WriteAsync(bytes, 0, read);
  164. // // Update our progress data and UI
  165. // UploadedBytes += read;
  166. // percent = UploadedBytes * 100 / TotalBytes;
  167. // // Report progress with a string
  168. // LargeUploadMessage = $"Uploading {args.File.Name} {percent}%";
  169. // await InvokeAsync(StateHasChanged);
  170. // }
  171. // }
  172. // }
  173. var file = args.File;
  174. var buffer = new byte[1 * 1024 * 1024]; // 80 KB buffer size 1 * 1024 * 1024
  175. var totalRead = 0L;
  176. using (var inStream = file.OpenReadStream(long.MaxValue))
  177. using (var outStream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
  178. {
  179. while (totalRead < file.Size)
  180. {
  181. var read = await inStream.ReadAsync(buffer, 0, buffer.Length);
  182. if (read == 0)
  183. break;
  184. await outStream.WriteAsync(buffer, 0, read);
  185. totalRead += read;
  186. // Atualize o progresso se necessário
  187. percent = (int)((totalRead * 100) / file.Size);
  188. LargeUploadMessage = $"Uploading {args.File.Name} {percent}%";
  189. Console.WriteLine($"Uploading {file.Name} {percent}%");
  190. }
  191. }
  192. LargeUploadMessage = "Upload Completo.";
  193. //await ListFiles();
  194. Documento documentoGravado = new()
  195. {
  196. Descricao = "",
  197. ArquivoNomeOriginal = newFileNameWithoutPath,
  198. ArquivoTamanhoBytes = TotalBytes,
  199. ProjetoId = projeto.Id,
  200. TipoDocumentoId = modelDocument.TipoDocumento.Id,
  201. UploadCompleto = true
  202. };
  203. if (documentoGravado.Id > 0) // Alteração
  204. {
  205. documentoGravado.AtribuiId(modelDocument.DocumentoId);
  206. }
  207. resultado = documentoService.Gravar(documentoGravado);
  208. //UploadingLargeFile = false;
  209. tipoDocumentoLoading.Uploading = false;
  210. bool uploadIncompletosAntes = _documentosModel.Any(x => !x.Status);
  211. AtualizarListaDocumentos();
  212. bool uploadIncompletosDepois = _documentosModel.Any(x => !x.Status);
  213. if (uploadIncompletosAntes && !uploadIncompletosDepois)
  214. {
  215. //"Seu projeto foi enviado com sucesso.Caso deseje realizar alguma alteração, o projeto estará listado na área 'meus projetos'"
  216. await dialogService.Alert("Seu projeto foi enviado com sucesso. <br />Caso deseje realizar alguma alteração, o projeto estará listado na área 'Meus Projetos'.",
  217. "Projeto Enviado",
  218. new AlertOptions() { OkButtonText = "Ok" });
  219. Navigation.NavigateTo($"/gestaoprojetos");
  220. return;
  221. }
  222. if (!uploadIncompletosAntes)
  223. {
  224. // "Deseja alterar mais algum anexo enviado?
  225. // sim => permanece na tela
  226. // não => exibe mensagem anterior "primeira vez que ele completa."
  227. bool? result = await dialogService.Confirm("Deseja alterar mais algum anexo enviado?",
  228. "Anexo Enviado com Sucesso",
  229. new ConfirmOptions() { OkButtonText = "Yes", CancelButtonText = "No" });
  230. if (result != true)
  231. {
  232. await dialogService.Alert("Seu projeto foi enviado com sucesso.</p>Caso deseje realizar alguma alteração, o projeto estará listado na área 'Meus Projetos'",
  233. "Projeto Enviado",
  234. new AlertOptions() { OkButtonText = "Ok" });
  235. Navigation.NavigateTo($"/gestaoprojetos");
  236. return;
  237. }
  238. return;
  239. }
  240. }
  241. private void GerarArquivoTextoParaProjeto(Candidato candidato, Projeto projeto, string path)
  242. {
  243. string saudacao = $@"
  244. Candidado:
  245. Nome......: {candidato.NomeCompleto}
  246. E-mail....: {candidato.Email}
  247. Telefone..: {candidato.Email}
  248. ";
  249. if (candidato.TipoPessoa) // Representa empresa
  250. {
  251. saudacao += $@"
  252. Empresa:
  253. Nome....: {candidato.EmpresaRazaoSocial}
  254. E-mail..: {candidato.EmpresaEmail}
  255. Telefone..: {candidato.EmpresaTelefone}";
  256. }
  257. saudacao += $@"
  258. Projeto '{projeto.Nome}':
  259. Responsável.....: {projeto.ResponsavelTecnicoNomeCompleto}
  260. E-mail...: {projeto.ResponsavelTecnicoEmail}
  261. Telefone.: {projeto.ResponsavelTecnicoTelefone}";
  262. string fullPath = Path.Combine(path, "config.txt");
  263. File.WriteAllText(fullPath, saudacao);
  264. }
  265. [Parameter]
  266. public Guid projetoCodigo { get; set; }
  267. private Candidato? _candidato;
  268. private Projeto? _projeto;
  269. private IEnumerable<Documento>? _documentos;
  270. private IEnumerable<InputModelDocuments>? _documentosModel;
  271. private string _mensagem = "Loading...";
  272. private long candidatoId;
  273. private string rotuloBotaoGravar = "Enviar";
  274. //private long projetoId = 5;
  275. List<ModelTipoDocumento> listaTipoDocumentoLoading = new();
  276. [SupplyParameterFromForm]
  277. private InputModel Input { get; set; } = new();
  278. private List<int> listaDeUnidades = Enumerable.Range(2, 31).ToList();
  279. protected override async Task OnInitializedAsync()
  280. {
  281. var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
  282. var user = authState.User;
  283. if (user.Identity.IsAuthenticated)
  284. {
  285. var identityUser = await UserManager.GetUserAsync(user);
  286. if (identityUser != null)
  287. {
  288. _candidato = candidatoService.ObterPorUserID(identityUser.Id);
  289. }
  290. }
  291. if (projetoCodigo != Guid.Empty)
  292. {
  293. _projeto = projetoService.ObterPorCodigo(projetoCodigo);
  294. _documentos = documentoService.Listar(_projeto.Id);
  295. AtualizarModel(_projeto);
  296. rotuloBotaoGravar = "Alterar Dados do Projeto Cadastrado";
  297. }
  298. AtualizarListaDocumentos();
  299. var listaTipo = tipoDocumentoServico.Listar();
  300. foreach (var tipo in listaTipo)
  301. listaTipoDocumentoLoading.Add(new ModelTipoDocumento { TipoDocumento = tipo, Uploading = false });
  302. }
  303. private void AtualizarModel(Projeto projeto)
  304. {
  305. Input.Id = projeto.Id;
  306. Input.Nome = projeto.Nome;
  307. Input.NumeroUnidades = projeto.NumeroUnidades;
  308. Input.ResponsavelTecnicoCpf = projeto.ResponsavelTecnicoCpf;
  309. Input.ResponsavelTecnicoNomeCompleto = projeto.ResponsavelTecnicoNomeCompleto;
  310. Input.ResponsavelTecnicoTelefone = projeto.ResponsavelTecnicoTelefone;
  311. Input.ResponsavelTecnicoEmail = projeto.ResponsavelTecnicoEmail;
  312. }
  313. private async Task OnSubmit(InputModel model)
  314. {
  315. Projeto resultado;
  316. Projeto projeto = new(_candidato.Id)
  317. {
  318. Codigo = Guid.NewGuid(),
  319. Nome = Input.Nome,
  320. NumeroUnidades = Input.NumeroUnidades,
  321. ResponsavelTecnicoCpf = Input.ResponsavelTecnicoCpf.Replace(".", "").Replace("-", ""),
  322. ResponsavelTecnicoNomeCompleto = Input.ResponsavelTecnicoNomeCompleto,
  323. ResponsavelTecnicoTelefone = Input.ResponsavelTecnicoTelefone,
  324. ResponsavelTecnicoEmail = Input.ResponsavelTecnicoEmail
  325. };
  326. if (_projeto != null) // Alteração
  327. {
  328. projeto.AtribuiId(_projeto.Id);
  329. resultado = projetoService.Alterar(projeto);
  330. }
  331. else // Inclusão
  332. {
  333. resultado = projetoService.Adicionar(projeto);
  334. rotuloBotaoGravar = "Alterar Dados do Projeto Cadastrado";
  335. }
  336. if (resultado != null)
  337. {
  338. _projeto = resultado;
  339. projetoCodigo = resultado.Codigo;
  340. MontarModelDocuments();
  341. NotificationMessage message = new NotificationMessage
  342. {
  343. Severity = NotificationSeverity.Info,
  344. Summary = "Projeto gravado com sucesso.",
  345. Detail = "",
  346. Duration = 3000
  347. };
  348. notificationService.Notify(message);
  349. }
  350. StateHasChanged();
  351. }
  352. private async void OnInvalidSubmit(FormInvalidSubmitEventArgs args)
  353. {
  354. //Log("InvalidSubmit", JsonSerializer.Serialize(args, new JsonSerializerOptions() { WriteIndented = true }));
  355. }
  356. private void AtualizarListaDocumentos()
  357. {
  358. if (_projeto == null || _projeto.Id <= 0)
  359. return;
  360. _documentos = documentoService.Listar(_projeto.Id);
  361. MontarModelDocuments();
  362. StateHasChanged();
  363. }
  364. private void MontarModelDocuments()
  365. {
  366. List<InputModelDocuments> resultado = new();
  367. var listaTipo = tipoDocumentoServico.Listar();
  368. foreach (TipoDocumento tipo in listaTipo)
  369. {
  370. long documentoId = 0;
  371. bool status = false;
  372. if (_documentos != null && _documentos.Count() > 0)
  373. {
  374. var ProjetoPossuiDocumento = _documentos.Where(d => d.TipoDocumentoId == tipo.Id).SingleOrDefault();
  375. if (ProjetoPossuiDocumento != null)
  376. {
  377. documentoId = ProjetoPossuiDocumento.Id;
  378. status = true;
  379. }
  380. }
  381. resultado.Add(new InputModelDocuments
  382. {
  383. DocumentoId = documentoId,
  384. TipoDocumento = tipo,
  385. Status = status
  386. });
  387. }
  388. _documentosModel = resultado;
  389. }
  390. private sealed class InputModel
  391. {
  392. public long Id { get; set; }
  393. [Required]
  394. [StringLength(100, MinimumLength = 5)]
  395. [Display(Name = "Nome do Projeto")]
  396. public string Nome { get; set; } = "";
  397. [Required]
  398. [Range(2, 32, ErrorMessage = "O valor deve estar entre 2 e 32.")]
  399. [Display(Name = "Número de Unidades Habitacionais por Pavimento")]
  400. public int NumeroUnidades { get; set; } = 2;
  401. [Required]
  402. [MaxLength(100)]
  403. [Display(Name = "Nome do Responsável Técnico da Empresa")]
  404. public string ResponsavelTecnicoNomeCompleto { get; set; }
  405. [Required]
  406. [MaxLength(100)]
  407. [EmailAddress]
  408. [Display(Name = "E-mail do Responsável Técnico da Empresa")]
  409. public string ResponsavelTecnicoEmail { get; set; }
  410. [Required]
  411. [MaxLength(14)]
  412. [Display(Name = "CPF do Responsável Técnico da Empresa")]
  413. public string ResponsavelTecnicoCpf { get; set; }
  414. [Required]
  415. [MaxLength(20)]
  416. [Display(Name = "Telefone do Responsável Técnico da Empresa")]
  417. public string ResponsavelTecnicoTelefone { get; set; }
  418. }
  419. private sealed class InputModelDocuments
  420. {
  421. public long DocumentoId { get; set; }
  422. public TipoDocumento TipoDocumento { get; set; }
  423. //public DateTime DataInclusao { get; set; }
  424. public bool Status { get; set; }
  425. }
  426. private sealed class ModelTipoDocumento
  427. {
  428. public TipoDocumento TipoDocumento { get; set; }
  429. public bool Uploading { get; set; }
  430. }
  431. }