PageProjeto.razor 19 KB

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