Compare commits
	
		
			5 Commits
		
	
	
		
			mrb
			...
			538b1f817e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 538b1f817e | |||
| 89790f4fc1 | |||
| 9b7e3ef897 | |||
| ba8d92ea01 | |||
| 603052d7e5 | 
							
								
								
									
										229
									
								
								.editorconfig
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								.editorconfig
									
									
									
									
									
								
							| @ -1,229 +0,0 @@ | ||||
| # Remove the line below if you want to inherit .editorconfig settings from higher directories | ||||
| root = true | ||||
|  | ||||
| # C# files | ||||
| [*.cs] | ||||
|  | ||||
| #### Core EditorConfig Options #### | ||||
|  | ||||
| # Indentation and spacing | ||||
| indent_size = 4 | ||||
| indent_style = space | ||||
| tab_width = 4 | ||||
|  | ||||
| # New line preferences | ||||
| end_of_line = crlf | ||||
| insert_final_newline = false | ||||
|  | ||||
| #### .NET Coding Conventions #### | ||||
|  | ||||
| # Organize usings | ||||
| dotnet_separate_import_directive_groups = true | ||||
| dotnet_sort_system_directives_first = true | ||||
| file_header_template = unset | ||||
|  | ||||
| # this. and Me. preferences | ||||
| dotnet_style_qualification_for_event = true | ||||
| dotnet_style_qualification_for_field = true | ||||
| dotnet_style_qualification_for_method = true | ||||
| dotnet_style_qualification_for_property = true | ||||
|  | ||||
| # Language keywords vs BCL types preferences | ||||
| dotnet_style_predefined_type_for_locals_parameters_members = true | ||||
| dotnet_style_predefined_type_for_member_access = true | ||||
|  | ||||
| # Parentheses preferences | ||||
| dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:error | ||||
| dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:error | ||||
| dotnet_style_parentheses_in_other_operators = never_if_unnecessary | ||||
| dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:error | ||||
|  | ||||
| # Modifier preferences | ||||
| dotnet_style_require_accessibility_modifiers = for_non_interface_members | ||||
|  | ||||
| # Expression-level preferences | ||||
| dotnet_style_coalesce_expression = false | ||||
| dotnet_style_collection_initializer = true | ||||
| dotnet_style_explicit_tuple_names = true:error | ||||
| dotnet_style_namespace_match_folder = true | ||||
| dotnet_style_null_propagation = true | ||||
| dotnet_style_object_initializer = true | ||||
| dotnet_style_operator_placement_when_wrapping = beginning_of_line | ||||
| dotnet_style_prefer_auto_properties = true | ||||
| dotnet_style_prefer_compound_assignment = true | ||||
| dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion | ||||
| dotnet_style_prefer_conditional_expression_over_return = true:suggestion | ||||
| dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed | ||||
| dotnet_style_prefer_inferred_anonymous_type_member_names = false | ||||
| dotnet_style_prefer_inferred_tuple_names = false | ||||
| dotnet_style_prefer_is_null_check_over_reference_equality_method = true | ||||
| dotnet_style_prefer_simplified_boolean_expressions = true | ||||
| dotnet_style_prefer_simplified_interpolation = true | ||||
|  | ||||
| # Field preferences | ||||
| dotnet_style_readonly_field = true:warning | ||||
|  | ||||
| # Parameter preferences | ||||
| dotnet_code_quality_unused_parameters = all:error | ||||
|  | ||||
| # Suppression preferences | ||||
| dotnet_remove_unnecessary_suppression_exclusions = none | ||||
|  | ||||
| # New line preferences | ||||
| dotnet_style_allow_multiple_blank_lines_experimental = false:error | ||||
| dotnet_style_allow_statement_immediately_after_block_experimental = false:warning | ||||
|  | ||||
| #### C# Coding Conventions #### | ||||
|  | ||||
| # var preferences | ||||
| csharp_style_var_elsewhere = false:error | ||||
| csharp_style_var_for_built_in_types = false:error | ||||
| csharp_style_var_when_type_is_apparent = false:error | ||||
|  | ||||
| # Expression-bodied members | ||||
| csharp_style_expression_bodied_accessors = false | ||||
| csharp_style_expression_bodied_constructors = false | ||||
| csharp_style_expression_bodied_indexers = false | ||||
| csharp_style_expression_bodied_lambdas = true | ||||
| csharp_style_expression_bodied_local_functions = false | ||||
| csharp_style_expression_bodied_methods = when_on_single_line:suggestion | ||||
| csharp_style_expression_bodied_operators = false | ||||
| csharp_style_expression_bodied_properties = false | ||||
|  | ||||
| # Pattern matching preferences | ||||
| csharp_style_pattern_matching_over_as_with_null_check = false | ||||
| csharp_style_pattern_matching_over_is_with_cast_check = false | ||||
| csharp_style_prefer_extended_property_pattern = true | ||||
| csharp_style_prefer_not_pattern = true | ||||
| csharp_style_prefer_pattern_matching = true | ||||
| csharp_style_prefer_switch_expression = false | ||||
|  | ||||
| # Null-checking preferences | ||||
| csharp_style_conditional_delegate_call = false | ||||
|  | ||||
| # Modifier preferences | ||||
| csharp_prefer_static_local_function = false | ||||
| csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async | ||||
| csharp_style_prefer_readonly_struct = true:warning | ||||
| csharp_style_prefer_readonly_struct_member = true | ||||
|  | ||||
| # Code-block preferences | ||||
| csharp_prefer_braces = when_multiline:error | ||||
| csharp_prefer_simple_using_statement = false | ||||
| csharp_style_namespace_declarations = file_scoped:error | ||||
| csharp_style_prefer_method_group_conversion = true:suggestion | ||||
| csharp_style_prefer_top_level_statements = true:error | ||||
|  | ||||
| # Expression-level preferences | ||||
| csharp_prefer_simple_default_expression = true | ||||
| csharp_style_deconstructed_variable_declaration = false | ||||
| csharp_style_implicit_object_creation_when_type_is_apparent = false | ||||
| csharp_style_inlined_variable_declaration = true | ||||
| csharp_style_prefer_index_operator = false:error | ||||
| csharp_style_prefer_local_over_anonymous_function = true:error | ||||
| csharp_style_prefer_null_check_over_type_check = true | ||||
| csharp_style_prefer_range_operator = false:error | ||||
| csharp_style_prefer_tuple_swap = true | ||||
| csharp_style_prefer_utf8_string_literals = true | ||||
| csharp_style_throw_expression = false | ||||
| csharp_style_unused_value_assignment_preference = unused_local_variable | ||||
| csharp_style_unused_value_expression_statement_preference = unused_local_variable | ||||
|  | ||||
| # 'using' directive preferences | ||||
| csharp_using_directive_placement = outside_namespace:error | ||||
|  | ||||
| # New line preferences | ||||
| csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true | ||||
| csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true | ||||
| csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true | ||||
| csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:error | ||||
| csharp_style_allow_embedded_statements_on_same_line_experimental = true | ||||
|  | ||||
| #### C# Formatting Rules #### | ||||
|  | ||||
| # New line preferences | ||||
| csharp_new_line_before_catch = false | ||||
| csharp_new_line_before_else = false | ||||
| csharp_new_line_before_finally = false | ||||
| csharp_new_line_before_members_in_anonymous_types = true | ||||
| csharp_new_line_before_members_in_object_initializers = true | ||||
| csharp_new_line_before_open_brace = none | ||||
| csharp_new_line_between_query_expression_clauses = true | ||||
|  | ||||
| # Indentation preferences | ||||
| csharp_indent_block_contents = true | ||||
| csharp_indent_braces = false | ||||
| csharp_indent_case_contents = true | ||||
| csharp_indent_case_contents_when_block = true | ||||
| csharp_indent_labels = one_less_than_current | ||||
| csharp_indent_switch_labels = true | ||||
|  | ||||
| # Space preferences | ||||
| csharp_space_after_cast = false | ||||
| csharp_space_after_colon_in_inheritance_clause = true | ||||
| csharp_space_after_comma = true | ||||
| csharp_space_after_dot = false | ||||
| csharp_space_after_keywords_in_control_flow_statements = true | ||||
| csharp_space_after_semicolon_in_for_statement = true | ||||
| csharp_space_around_binary_operators = before_and_after | ||||
| csharp_space_around_declaration_statements = false | ||||
| csharp_space_before_colon_in_inheritance_clause = true | ||||
| csharp_space_before_comma = false | ||||
| csharp_space_before_dot = false | ||||
| csharp_space_before_open_square_brackets = false | ||||
| csharp_space_before_semicolon_in_for_statement = false | ||||
| csharp_space_between_empty_square_brackets = false | ||||
| csharp_space_between_method_call_empty_parameter_list_parentheses = false | ||||
| csharp_space_between_method_call_name_and_opening_parenthesis = false | ||||
| csharp_space_between_method_call_parameter_list_parentheses = false | ||||
| csharp_space_between_method_declaration_empty_parameter_list_parentheses = false | ||||
| csharp_space_between_method_declaration_name_and_open_parenthesis = false | ||||
| csharp_space_between_method_declaration_parameter_list_parentheses = false | ||||
| csharp_space_between_parentheses = false | ||||
| csharp_space_between_square_brackets = false | ||||
|  | ||||
| # Wrapping preferences | ||||
| csharp_preserve_single_line_blocks = true | ||||
| csharp_preserve_single_line_statements = true | ||||
|  | ||||
| #### Naming styles #### | ||||
|  | ||||
| # Naming rules | ||||
|  | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i | ||||
|  | ||||
| dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.types_should_be_pascal_case.symbols = types | ||||
| dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| # Symbol specifications | ||||
|  | ||||
| dotnet_naming_symbols.interface.applicable_kinds = interface | ||||
| dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.interface.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum | ||||
| dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.types.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method | ||||
| dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.non_field_members.required_modifiers =  | ||||
|  | ||||
| # Naming styles | ||||
|  | ||||
| dotnet_naming_style.pascal_case.required_prefix =  | ||||
| dotnet_naming_style.pascal_case.required_suffix =  | ||||
| dotnet_naming_style.pascal_case.word_separator =  | ||||
| dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.begins_with_i.required_prefix = I | ||||
| dotnet_naming_style.begins_with_i.required_suffix =  | ||||
| dotnet_naming_style.begins_with_i.word_separator =  | ||||
| dotnet_naming_style.begins_with_i.capitalization = pascal_case | ||||
|  | ||||
							
								
								
									
										19
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -337,6 +337,23 @@ ASALocalRun/ | ||||
| !.vscode/tasks.json | ||||
| !.vscode/launch.json | ||||
| !.vscode/extensions.json | ||||
| .vscode | ||||
|  | ||||
| .env | ||||
|  | ||||
| /Fab2ApprovalMKLink/App_Start | ||||
| /Fab2ApprovalMKLink/Controllers | ||||
| /Fab2ApprovalMKLink/DMO | ||||
| /Fab2ApprovalMKLink/Jobs | ||||
| /Fab2ApprovalMKLink/JobSchedules | ||||
| /Fab2ApprovalMKLink/Misc | ||||
| /Fab2ApprovalMKLink/Models | ||||
| /Fab2ApprovalMKLink/PdfGenerator | ||||
| /Fab2ApprovalMKLink/Utilities | ||||
| /Fab2ApprovalMKLink/ViewModels | ||||
|  | ||||
| /Fab2ApprovalMKLink/.vscode/.UserSecrets | ||||
| /Fab2ApprovalSystem/.vscode/.UserSecrets | ||||
| /Fab2ApprovalTests/.vscode/.UserSecrets | ||||
| /MesaFabApproval.API/.vscode/.UserSecrets | ||||
| /MesaFabApproval.Client/.vscode/.UserSecrets | ||||
| /MesaFabApproval.Shared/.vscode/.UserSecrets | ||||
							
								
								
									
										41
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| { | ||||
|     // Use IntelliSense to learn about possible attributes. | ||||
|     // Hover to view descriptions of existing attributes. | ||||
|     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|     "version": "0.2.0", | ||||
|     "configurations": [ | ||||
|         { | ||||
|             "name": ".NET Core Launch (console) - Fab2ApprovalSystem", | ||||
|             "type": "coreclr", | ||||
|             "request": "launch", | ||||
|             "preLaunchTask": "Fab2ApprovalSystem-build", | ||||
|             "program": "${workspaceFolder}/Fab2ApprovalSystem/bin/Debug/net8.0/win-x64/Fab2ApprovalSystem.dll", | ||||
|             "args": [], | ||||
|             "cwd": "${workspaceFolder}", | ||||
|             "console": "integratedTerminal", | ||||
|             "stopAtEntry": false | ||||
|         }, | ||||
|         { | ||||
|             "name": ".NET Core Launch (console) - MesaFabApproval.API", | ||||
|             "type": "coreclr", | ||||
|             "request": "launch", | ||||
|             "preLaunchTask": "MesaFabApproval.API-build", | ||||
|             "program": "${workspaceFolder}/MesaFabApproval.API/bin/Debug/net8.0/MesaFabApproval.API.dll", | ||||
|             "args": [], | ||||
|             "cwd": "${workspaceFolder}", | ||||
|             "console": "integratedTerminal", | ||||
|             "stopAtEntry": false | ||||
|         }, | ||||
|         { | ||||
|             "name": ".NET Core Attach", | ||||
|             "type": "coreclr", | ||||
|             "request": "attach" | ||||
|         }, | ||||
|         { | ||||
|             "type": "node", | ||||
|             "request": "launch", | ||||
|             "name": "node Launch Current Opened File", | ||||
|             "program": "${file}" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										424
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										424
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,424 @@ | ||||
| { | ||||
|   "[markdown]": { | ||||
|     "editor.wordWrap": "off" | ||||
|   }, | ||||
|   "files.exclude": { | ||||
|     "**/.git": false, | ||||
|     "**/node_modules": true | ||||
|   }, | ||||
|   "files.watcherExclude": { | ||||
|     "**/node_modules": true | ||||
|   }, | ||||
|   "cSpell.words": [ | ||||
|     "abutton", | ||||
|     "accessibilities", | ||||
|     "accodingly", | ||||
|     "acknowledgmentby", | ||||
|     "Acks", | ||||
|     "actionsheet", | ||||
|     "Additonal", | ||||
|     "Addtional", | ||||
|     "againm", | ||||
|     "agendaview", | ||||
|     "Antlr", | ||||
|     "Appover", | ||||
|     "Appprrovers", | ||||
|     "Approvalog", | ||||
|     "Aprovers", | ||||
|     "Aproverslist", | ||||
|     "asax", | ||||
|     "aspnetmvc", | ||||
|     "Assignedto", | ||||
|     "Atachments", | ||||
|     "Attachemnt", | ||||
|     "Attachemnts", | ||||
|     "Attchment", | ||||
|     "auditee", | ||||
|     "Auditee", | ||||
|     "Auditees", | ||||
|     "automaically", | ||||
|     "Autthorized", | ||||
|     "beacuase", | ||||
|     "beforeunload", | ||||
|     "Belguim", | ||||
|     "bfound", | ||||
|     "bgcolor", | ||||
|     "Bies", | ||||
|     "binded", | ||||
|     "blackbackground", | ||||
|     "blackhover", | ||||
|     "blackpressed", | ||||
|     "blueenergy", | ||||
|     "blueopal", | ||||
|     "buttongroup", | ||||
|     "BYMRB", | ||||
|     "bytesgot", | ||||
|     "CAID", | ||||
|     "casection", | ||||
|     "CAXXXX", | ||||
|     "CCPCR", | ||||
|     "CCPCRB", | ||||
|     "cellspacing", | ||||
|     "Cheeso's", | ||||
|     "chkbx", | ||||
|     "Clib", | ||||
|     "colorpicker", | ||||
|     "columnmenu", | ||||
|     "columnsreorder", | ||||
|     "columnsresize", | ||||
|     "comming", | ||||
|     "Containmen", | ||||
|     "Copmments", | ||||
|     "correctiv", | ||||
|     "Correctivet", | ||||
|     "Creat", | ||||
|     "currentd", | ||||
|     "Cyle", | ||||
|     "dadada", | ||||
|     "darkbluehover", | ||||
|     "darkbluepressed", | ||||
|     "darkred", | ||||
|     "datafields", | ||||
|     "datasource", | ||||
|     "dataviz", | ||||
|     "datepicker", | ||||
|     "datetimepicker", | ||||
|     "dayview", | ||||
|     "Deletet", | ||||
|     "Delgation", | ||||
|     "DENITED", | ||||
|     "Deparmtent", | ||||
|     "departmentids", | ||||
|     "Descirption", | ||||
|     "devprog", | ||||
|     "dfeffc", | ||||
|     "Disp", | ||||
|     "Dispo", | ||||
|     "Dispoitio", | ||||
|     "Dispos", | ||||
|     "Dispositon", | ||||
|     "Dispostion", | ||||
|     "Dispostions", | ||||
|     "dispotypevalidation", | ||||
|     "Docbase", | ||||
|     "Documentum", | ||||
|     "Documetum", | ||||
|     "dont", | ||||
|     "downlaoded", | ||||
|     "draganddrop", | ||||
|     "dragcancel", | ||||
|     "dropdownlist", | ||||
|     "Eamils", | ||||
|     "ECNPCRB", | ||||
|     "Ecns", | ||||
|     "edmx", | ||||
|     "EECN", | ||||
|     "emai", | ||||
|     "emailparams", | ||||
|     "Emergrncy", | ||||
|     "energyblue", | ||||
|     "Eran", | ||||
|     "Esql", | ||||
|     "ETECN", | ||||
|     "EXCELOPENXML", | ||||
|     "existinglocation", | ||||
|     "Expando", | ||||
|     "extrafield", | ||||
|     "fadc", | ||||
|     "fbec", | ||||
|     "fcfdfd", | ||||
|     "fdff", | ||||
|     "fece", | ||||
|     "feeebd", | ||||
|     "ffdc", | ||||
|     "ffdd", | ||||
|     "fieldset", | ||||
|     "FILIPE", | ||||
|     "filtermenu", | ||||
|     "Fldr", | ||||
|     "flintstone", | ||||
|     "FLOWLOCS", | ||||
|     "FMEA", | ||||
|     "ftplib", | ||||
|     "FTPSPN", | ||||
|     "GETDATE", | ||||
|     "gitea", | ||||
|     "globaldocudms", | ||||
|     "glyphicons", | ||||
|     "groupable", | ||||
|     "Guids", | ||||
|     "halflings", | ||||
|     "Hexsize", | ||||
|     "highcontrast", | ||||
|     "Hmac", | ||||
|     "holdsteps", | ||||
|     "hostspecific", | ||||
|     "icenium", | ||||
|     "IECN", | ||||
|     "imagebrowser", | ||||
|     "IMRB", | ||||
|     "Infineon", | ||||
|     "Insertd", | ||||
|     "inverseicons", | ||||
|     "IPCRB", | ||||
|     "ISADMIN", | ||||
|     "islast", | ||||
|     "ISNULL", | ||||
|     "ITAR", | ||||
|     "jquery", | ||||
|     "jqueryval", | ||||
|     "jqwidgets", | ||||
|     "jqxbuttongroup", | ||||
|     "jqxbuttons", | ||||
|     "jqxcalendar", | ||||
|     "jqxchart", | ||||
|     "jqxcheckbox", | ||||
|     "jqxcolorpicker", | ||||
|     "jqxcombobox", | ||||
|     "jqxcore", | ||||
|     "jqxdata", | ||||
|     "jqxdatatable", | ||||
|     "jqxdatetimeinput", | ||||
|     "jqxdocking", | ||||
|     "jqxdockpanel", | ||||
|     "jqxdragdrop", | ||||
|     "jqxdropdownbutton", | ||||
|     "jqxdropdownlist", | ||||
|     "jqxexpander", | ||||
|     "jqxgauge", | ||||
|     "jqxgrid", | ||||
|     "jqxinput", | ||||
|     "jqxknockout", | ||||
|     "jqxlistbox", | ||||
|     "jqxlistmenu", | ||||
|     "jqxmaskedinput", | ||||
|     "jqxmenu", | ||||
|     "jqxnavigationbar", | ||||
|     "jqxnumberinput", | ||||
|     "jqxpanel", | ||||
|     "jqxpasswordinput", | ||||
|     "jqxprogressbar", | ||||
|     "jqxradiobutton", | ||||
|     "jqxrangeselector", | ||||
|     "jqxrating", | ||||
|     "jqxresponse", | ||||
|     "jqxscrollbar", | ||||
|     "jqxscrollview", | ||||
|     "jqxslider", | ||||
|     "jqxsplitter", | ||||
|     "jqxswitchbutton", | ||||
|     "jqxtabs", | ||||
|     "jqxtooltip", | ||||
|     "jqxtouch", | ||||
|     "jqxtree", | ||||
|     "jqxtreegrid", | ||||
|     "jqxtreemap", | ||||
|     "jqxvalidator", | ||||
|     "jqxwindow", | ||||
|     "kendogridcustom", | ||||
|     "kendoui", | ||||
|     "labelelement", | ||||
|     "labelledby", | ||||
|     "Leanred", | ||||
|     "lightgray", | ||||
|     "linkbutton", | ||||
|     "Linq", | ||||
|     "Listdiv", | ||||
|     "listview", | ||||
|     "Lnks", | ||||
|     "localfilename", | ||||
|     "loclist", | ||||
|     "logis", | ||||
|     "logtext", | ||||
|     "loopmis", | ||||
|     "lotdispo", | ||||
|     "LOTDISPSITION", | ||||
|     "Lotfile", | ||||
|     "lotlist", | ||||
|     "lotstatusoption", | ||||
|     "LTRIM", | ||||
|     "MADUREIRA", | ||||
|     "mailrelay", | ||||
|     "MDTM", | ||||
|     "meego", | ||||
|     "meetingid", | ||||
|     "menubutton", | ||||
|     "mesafi", | ||||
|     "metroblack", | ||||
|     "metrodark", | ||||
|     "miliseconds", | ||||
|     "modalview", | ||||
|     "modernizr", | ||||
|     "Modernizr", | ||||
|     "monthview", | ||||
|     "MRBIs", | ||||
|     "Mrbs", | ||||
|     "msecs", | ||||
|     "multipleextended", | ||||
|     "Navigatable", | ||||
|     "nbsp", | ||||
|     "newbase", | ||||
|     "newchange", | ||||
|     "newdi", | ||||
|     "newfilename", | ||||
|     "newsource", | ||||
|     "Newtonsoft", | ||||
|     "notications", | ||||
|     "Notifcation", | ||||
|     "Notifyf", | ||||
|     "NTLM", | ||||
|     "Nullcc", | ||||
|     "numerictextbox", | ||||
|     "objdata", | ||||
|     "OCAP", | ||||
|     "occured", | ||||
|     "odata", | ||||
|     "oldfilename", | ||||
|     "OLHOLD", | ||||
|     "onclick", | ||||
|     "onmousemove", | ||||
|     "OPDESC", | ||||
|     "OPENQUERY", | ||||
|     "Oper", | ||||
|     "operationslist", | ||||
|     "Orginator", | ||||
|     "Originatorname", | ||||
|     "Ouellette", | ||||
|     "Owin", | ||||
|     "pageable", | ||||
|     "Pageable", | ||||
|     "panelbar", | ||||
|     "parentid", | ||||
|     "parminput", | ||||
|     "parms", | ||||
|     "Parms", | ||||
|     "particula", | ||||
|     "pasv", | ||||
|     "PASV", | ||||
|     "PATINDEX", | ||||
|     "PCRB", | ||||
|     "PCRBID", | ||||
|     "pcrvalues", | ||||
|     "pdbonly", | ||||
|     "Preventitive", | ||||
|     "preventivet", | ||||
|     "Prevetative", | ||||
|     "proces", | ||||
|     "Processedl", | ||||
|     "procs", | ||||
|     "productfamilies", | ||||
|     "progess", | ||||
|     "progressbar", | ||||
|     "qrcode", | ||||
|     "Quanityt", | ||||
|     "rangebar", | ||||
|     "Recep", | ||||
|     "Recepient", | ||||
|     "recieved", | ||||
|     "recordlock", | ||||
|     "remotefilename", | ||||
|     "reorderable", | ||||
|     "reportform", | ||||
|     "reportslist", | ||||
|     "reportslistdiv", | ||||
|     "Reqquired", | ||||
|     "Reqs", | ||||
|     "Requiest", | ||||
|     "Responsibles", | ||||
|     "RETR", | ||||
|     "Revisioing", | ||||
|     "Revisioned", | ||||
|     "Revison", | ||||
|     "rgba", | ||||
|     "rkotian", | ||||
|     "RNFR", | ||||
|     "RNTO", | ||||
|     "Roless", | ||||
|     "roundbg", | ||||
|     "RTRIM", | ||||
|     "SAMDB", | ||||
|     "scroller", | ||||
|     "scrollview", | ||||
|     "seleced", | ||||
|     "selectionlog", | ||||
|     "Selectpart", | ||||
|     "sess", | ||||
|     "Sfisharepoint", | ||||
|     "shinyblack", | ||||
|     "showpassword", | ||||
|     "SIGNON", | ||||
|     "simpleparser", | ||||
|     "slddrw", | ||||
|     "sldprt", | ||||
|     "sortasc", | ||||
|     "sortascbutton", | ||||
|     "sortdesc", | ||||
|     "sortdescbutton", | ||||
|     "sortremove", | ||||
|     "sparkline", | ||||
|     "splitview", | ||||
|     "SPNMRB", | ||||
|     "SPNPDB", | ||||
|     "SSRS", | ||||
|     "Sssign", | ||||
|     "Staus", | ||||
|     "stylesheet", | ||||
|     "Submited", | ||||
|     "subrole", | ||||
|     "subroles", | ||||
|     "Succefully", | ||||
|     "Succesfully", | ||||
|     "sucessfully", | ||||
|     "SURP", | ||||
|     "Swashbuckle", | ||||
|     "SWRN", | ||||
|     "tabindex", | ||||
|     "tabstrip", | ||||
|     "Tahoma", | ||||
|     "taskcompleted", | ||||
|     "Tasklist", | ||||
|     "Taveler", | ||||
|     "TECN", | ||||
|     "TECNs", | ||||
|     "TEMIRWAP", | ||||
|     "tempecd", | ||||
|     "tempimplement", | ||||
|     "templabel", | ||||
|     "tempvalue", | ||||
|     "TEMSA", | ||||
|     "timepicker", | ||||
|     "Tobe", | ||||
|     "Toplevel", | ||||
|     "Totrav", | ||||
|     "trainingby", | ||||
|     "Traininglist", | ||||
|     "traininglistdiv", | ||||
|     "transanction", | ||||
|     "Trav", | ||||
|     "Traveller", | ||||
|     "Traverler", | ||||
|     "TRAVLELER", | ||||
|     "Travler", | ||||
|     "TREEVIEW", | ||||
|     "trigerred", | ||||
|     "ttinclude", | ||||
|     "Uhandled", | ||||
|     "Updat", | ||||
|     "Uplaod", | ||||
|     "Upto", | ||||
|     "userevents", | ||||
|     "userids", | ||||
|     "userlist", | ||||
|     "Validatable", | ||||
|     "valueelement", | ||||
|     "Variabls", | ||||
|     "Verdana", | ||||
|     "vgrid", | ||||
|     "viewmodel", | ||||
|     "vsdoc", | ||||
|     "whethere", | ||||
|     "windowsphone", | ||||
|     "Winsock", | ||||
|     "worlflow" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										500
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										500
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,500 @@ | ||||
| { | ||||
|     "version": "2.0.0", | ||||
|     "tasks": [ | ||||
|         { | ||||
|             "label": "MSBuild", | ||||
|             "command": "C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/MSBuild/Current/Bin/MSBuild.exe", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "/target:Build", | ||||
|                 "/restore:True", | ||||
|                 "/p:RestoreSources=https://artifactory.intra.infineon.com/artifactory/api/nuget/ngt-fi-package-main-vir/%3Bhttps://packagemanagement.eu.infineon.com:4430/api/v2/%3Bhttps://api.nuget.org/v3/index.json", | ||||
|                 "/detailedsummary", | ||||
|                 "/consoleloggerparameters:PerformanceSummary;ErrorsOnly;", | ||||
|                 "/property:Configuration=Debug;TargetFrameworkVersion=v4.8", | ||||
|                 "Fab2ApprovalSystem/Fab2ApprovalSystem.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalMKLink-User Secrets Init", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj", | ||||
|                 "init" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalMKLink-User Secrets Set", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj", | ||||
|                 "set", | ||||
|                 "_UserSecretsId", | ||||
|                 "f2da5035-aba9-4676-9f8d-d6689f84663d" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalMKLink-Format", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "--report", | ||||
|                 ".vscode", | ||||
|                 "--verbosity", | ||||
|                 "detailed", | ||||
|                 "--severity", | ||||
|                 "warn" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalMKLink-Format-Whitespaces", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "whitespace" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalMKLink-build", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalMKLink-publish", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalMKLink-watch", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "watch", | ||||
|                 "run", | ||||
|                 "--project", | ||||
|                 "${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalMKLink-Publish AOT", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "-r", | ||||
|                 "win-x64", | ||||
|                 "-c", | ||||
|                 "Release", | ||||
|                 "-p:PublishAot=true", | ||||
|                 "${workspaceFolder}/Fab2ApprovalMKLink/Fab2ApprovalMKLink.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-User Secrets Init", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj", | ||||
|                 "init" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-User Secrets Set", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj", | ||||
|                 "set", | ||||
|                 "_UserSecretsId", | ||||
|                 "3942d1fb-d585-40ae-8985-d276d1b94b77" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-Format", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "--report", | ||||
|                 ".vscode", | ||||
|                 "--verbosity", | ||||
|                 "detailed", | ||||
|                 "--severity", | ||||
|                 "warn" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-Format-Whitespaces", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "whitespace" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-build", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-testDebug", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-testRelease", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary", | ||||
|                 "-c", | ||||
|                 "Release" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-publish", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-watch", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "watch", | ||||
|                 "run", | ||||
|                 "--project", | ||||
|                 "${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Fab2ApprovalTests-Publish AOT", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "-r", | ||||
|                 "win-x64", | ||||
|                 "-c", | ||||
|                 "Release", | ||||
|                 "-p:PublishAot=true", | ||||
|                 "${workspaceFolder}/Fab2ApprovalTests/Fab2ApprovalTests.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-User Secrets Init", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj", | ||||
|                 "init" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-User Secrets Set", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj", | ||||
|                 "set", | ||||
|                 "_UserSecretsId", | ||||
|                 "0b98e1f2-95ed-4edd-8149-58cce51ca059" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-Format", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "--report", | ||||
|                 ".vscode", | ||||
|                 "--verbosity", | ||||
|                 "detailed", | ||||
|                 "--severity", | ||||
|                 "warn" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-Format-Whitespaces", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "whitespace" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-build", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-testDebug", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-testRelease", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary", | ||||
|                 "-c", | ||||
|                 "Release" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-publish", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-watch", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "watch", | ||||
|                 "run", | ||||
|                 "--project", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.API-Publish AOT", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "-r", | ||||
|                 "win-x64", | ||||
|                 "-c", | ||||
|                 "Release", | ||||
|                 "-p:PublishAot=true", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.Client-User Secrets Init", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj", | ||||
|                 "init" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.Client-Format", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "--report", | ||||
|                 ".vscode", | ||||
|                 "--verbosity", | ||||
|                 "detailed", | ||||
|                 "--severity", | ||||
|                 "warn" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.Client-Format-Whitespaces", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "whitespace" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.Client-build", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.Client-testDebug", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.Client-testRelease", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary", | ||||
|                 "-c", | ||||
|                 "Release" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.Client-publish", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.Client-watch", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "watch", | ||||
|                 "run", | ||||
|                 "--project", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "MesaFabApproval.Client-Publish AOT", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "-r", | ||||
|                 "win-x64", | ||||
|                 "-c", | ||||
|                 "Release", | ||||
|                 "-p:PublishAot=true", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -1,10 +1,6 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Web.Http; | ||||
| using System.Web.Http; | ||||
|  | ||||
| namespace Fab2ApprovalSystem | ||||
| { | ||||
| namespace Fab2ApprovalSystem { | ||||
|     public static class WebApiConfig | ||||
|     { | ||||
|         public static void Register(HttpConfiguration config) | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Security.Claims; | ||||
| using System.Threading.Tasks; | ||||
| using System.Web; | ||||
| @ -15,8 +14,8 @@ using Fab2ApprovalSystem.DMO; | ||||
| using Microsoft.AspNet.Identity.Owin; | ||||
| using System.Net.Http; | ||||
| using Newtonsoft.Json; | ||||
| using System.Net.Http.Headers; | ||||
| using System.Text; | ||||
| using System.Net; | ||||
|  | ||||
| namespace Fab2ApprovalSystem.Controllers { | ||||
|     [Authorize] | ||||
| @ -129,6 +128,86 @@ namespace Fab2ApprovalSystem.Controllers { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         [HttpPost] | ||||
|         [AllowAnonymous] | ||||
|         public async Task<HttpResponseMessage> ExternalAuthSetup(AuthAttempt authAttempt) { | ||||
|             try { | ||||
|                 bool isLoginValid; | ||||
|  | ||||
|                 HttpClient httpClient = HttpClientFactory.Create(); | ||||
|                 httpClient.BaseAddress = new Uri(_apiBaseUrl); | ||||
|  | ||||
|                 HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/refresh"); | ||||
|  | ||||
|                 request.Content = new StringContent(JsonConvert.SerializeObject(authAttempt), | ||||
|                                                                   Encoding.UTF8, | ||||
|                                                                   "application/json"); | ||||
|  | ||||
|                 HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request); | ||||
|  | ||||
|                 if (!httpResponseMessage.IsSuccessStatusCode) | ||||
|                     throw new Exception($"The authentication API failed, because {httpResponseMessage.ReasonPhrase}"); | ||||
|  | ||||
|                 string responseContent = await httpResponseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 LoginResult loginResult = JsonConvert.DeserializeObject<LoginResult>(responseContent); | ||||
|  | ||||
| #if(DEBUG) | ||||
|                 isLoginValid = true; | ||||
|  | ||||
| #endif | ||||
| #if (!DEBUG) | ||||
|  | ||||
|                 bool isIFX = false; | ||||
|                 //domainProvider = Membership.Providers["NA_ADMembershipProvider"]; | ||||
|                 //isLoginValid = domainProvider.ValidateUser(model.LoginID, model.Password);   | ||||
|  | ||||
|                 if (GlobalVars.DBConnection.ToUpper() == "TEST" || GlobalVars.DBConnection.ToUpper() == "QUALITY") { | ||||
|                     isLoginValid = true; | ||||
|                 } else { | ||||
|                     isLoginValid = loginResult.IsAuthenticated; | ||||
|                     if (isLoginValid) isIFX = true; | ||||
|  | ||||
|                 } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|                 if (isLoginValid) { | ||||
|                     UserAccountDMO userDMO = new UserAccountDMO(); | ||||
|                     LoginModel user = userDMO.GetUser(authAttempt.LoginID); | ||||
|                     if (user != null) { | ||||
|                         Session["JWT"] = loginResult.AuthTokens.JwtToken; | ||||
|                         Session["RefreshToken"] = loginResult.AuthTokens.RefreshToken; | ||||
|  | ||||
|                         Session[GlobalVars.SESSION_USERID] = user.UserID; | ||||
|                         Session[GlobalVars.SESSION_USERNAME] = user.FullName; | ||||
|                         Session[GlobalVars.IS_ADMIN] = user.IsAdmin; | ||||
|                         Session[GlobalVars.IS_MANAGER] = user.IsManager; | ||||
|                         Session[GlobalVars.OOO] = user.OOO; | ||||
|                         Session[GlobalVars.CAN_CREATE_PARTS_REQUEST] = user.IsAdmin || PartsRequestController.CanCreatePartsRequest(user.UserID); | ||||
|  | ||||
|                         FormsAuthentication.SetAuthCookie(user.LoginID, true); | ||||
|  | ||||
|                         return new HttpResponseMessage(HttpStatusCode.OK); | ||||
|                     } else { | ||||
|                         ModelState.AddModelError("", "The user name does not exist in the DB. Please contact the System Admin"); | ||||
|  | ||||
|                         return new HttpResponseMessage(HttpStatusCode.NotFound); | ||||
|                     } | ||||
|                 } else { | ||||
|                     ModelState.AddModelError("", "The user name or password provided is incorrect."); | ||||
|  | ||||
|                     return new HttpResponseMessage(HttpStatusCode.Unauthorized); | ||||
|                 } | ||||
|             } catch (Exception ex) { | ||||
|                 Functions.WriteEvent(@User.Identity.Name + " " + ex.InnerException, System.Diagnostics.EventLogEntryType.Error); | ||||
|                 EventLogDMO.Add(new WinEventLog() { IssueID = 99999, UserID = @User.Identity.Name, DocumentType = "Login", OperationType = "Error", Comments = "Reject - " + ex.Message }); | ||||
|                 ModelState.AddModelError("", ex.Message); | ||||
|  | ||||
|                 return new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // GET: /Account/Register | ||||
|         [AllowAnonymous] | ||||
|         public ActionResult Register() { | ||||
|  | ||||
| @ -102,53 +102,15 @@ namespace Fab2ApprovalSystem.Controllers | ||||
|         // GET: /MRB/Edit/5 | ||||
|         public ActionResult Edit(int issueID) | ||||
|         { | ||||
|             MRB mrb = new MRB(); | ||||
|             int isITARCompliant = 1; | ||||
|             ViewBag.Status = "Pending"; | ||||
|             ViewBag.IsApprover = "false"; | ||||
|             ViewBag.IsCloser = "false"; | ||||
|             string jwt = Session["JWT"].ToString(); | ||||
|             string encodedJwt = System.Net.WebUtility.UrlEncode(jwt); | ||||
|             string refreshToken = Session["RefreshToken"].ToString(); | ||||
|             string encodedRefreshToken = System.Net.WebUtility.UrlEncode(refreshToken); | ||||
|             string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ?? | ||||
|                 "https://localhost:7255"; | ||||
|             string mrbUrl = $"{wasmClientUrl}/redirect?jwt={encodedJwt}&refreshToken={encodedRefreshToken}&redirectPath=/mrb/{issueID}"; | ||||
|  | ||||
|             //ViewBag.IsApproved = "false"; | ||||
|             //ViewBag.IsClosed = "false"; | ||||
|             PopulateCloseToQDB(); | ||||
|             mrb = mrbDMO.GetMRBItem(issueID, out isITARCompliant, (int)Session[GlobalVars.SESSION_USERID]); | ||||
|             ViewBag.UserList = mrbDMO.GetUserList(); | ||||
|  | ||||
|             if (isITARCompliant == 0) // not ITAR Compliant | ||||
|             { | ||||
|                 return View("UnAuthorizedAccess"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (mrb.ApprovalStatus == (int)GlobalVars.ApprovalOption.Approved) | ||||
|                 { | ||||
|                     //ViewBag.IsApproved = "true"; | ||||
|                     ViewBag.Status = "Approved"; | ||||
|                 } | ||||
|                 else if (mrb.ApprovalStatus == (int)GlobalVars.ApprovalOption.Closed) | ||||
|                 { | ||||
|                     ViewBag.Status = "Closed"; | ||||
|                     //ViewBag.IsClosed = "true"; | ||||
|                 } | ||||
|                 List<ApproversListViewModel> userList = MiscDMO.GetApproversListByDocument(issueID, mrb.CurrentStep, (int)GlobalVars.DocumentType.MRB); | ||||
|                 ApproversListViewModel appUser = userList.Find(delegate (ApproversListViewModel al) { return al.UserID == (int)Session[GlobalVars.SESSION_USERID]; }); | ||||
|                 if (appUser != null) | ||||
|                 { | ||||
|                     ViewBag.IsApprover = "true"; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|  | ||||
|             // can edit | ||||
|             ViewBag.Owners = MiscDMO.GetUserList(); | ||||
|             ViewBag.Modules = mrbDMO.GetModules(); | ||||
|             //ViewBag.Dispositions = mrbDMO.GetDispositions(); | ||||
|             ViewBag.RiskAssessments = mrbDMO.GetRiskAssessments(); | ||||
|             ViewBag.PartGroups = mrbDMO.GetPartGroups(); | ||||
|             ViewBag.DispoTypes = mrbDMO.GetDispositions(issueID).Select(d => new { d.DispositionType }); | ||||
|  | ||||
|             return View(mrb); | ||||
|             return Redirect(mrbUrl); | ||||
|         } | ||||
|  | ||||
|         // | ||||
| @ -178,39 +140,15 @@ namespace Fab2ApprovalSystem.Controllers | ||||
|         /// <returns></returns> | ||||
|         public ActionResult ReadOnly(int issueID) | ||||
|         { | ||||
|             MRB mrb = new MRB(); | ||||
|             int isITARCompliant = 1; | ||||
|             string jwt = Session["JWT"].ToString(); | ||||
|             string encodedJwt = System.Net.WebUtility.UrlEncode(jwt); | ||||
|             string refreshToken = Session["RefreshToken"].ToString(); | ||||
|             string encodedRefreshToken = System.Net.WebUtility.UrlEncode(refreshToken); | ||||
|             string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ?? | ||||
|                 "https://localhost:7255"; | ||||
|             string mrbUrl = $"{wasmClientUrl}/redirect?jwt={encodedJwt}&refreshToken={encodedRefreshToken}&redirectPath=/mrb/{issueID}"; | ||||
|  | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 if (isITARCompliant == 0) // not ITAR Compliant | ||||
|                 { | ||||
|                     return View("UnAuthorizedAccess"); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     mrb = mrbDMO.GetMRBItem(issueID, out isITARCompliant, (int)Session[GlobalVars.SESSION_USERID]); | ||||
|  | ||||
|                     ViewBag.Owners = MiscDMO.GetUserList(); | ||||
|                     ViewBag.Modules = mrbDMO.GetModules(); | ||||
|                     //ViewBag.Dispositions = mrbDMO.GetDispositions(); | ||||
|                     ViewBag.RiskAssessments = mrbDMO.GetRiskAssessments(); | ||||
|                     ViewBag.PartGroups = mrbDMO.GetPartGroups(); | ||||
|                     ViewBag.DispoTypes = mrbDMO.GetDispositions(issueID).Select(d => new { d.DispositionType }); | ||||
|                 } | ||||
|  | ||||
|                 return View(mrb); | ||||
|             } | ||||
|  | ||||
|             catch (Exception e) | ||||
|             { | ||||
|                 string exceptionString = e.Message.ToString().Trim().Length > 500 ? "IssueID=" + issueID.ToString() + "  " + e.Message.ToString().Substring(0, 250) : e.Message.ToString(); | ||||
|                 Functions.WriteEvent(@User.Identity.Name + "\r\n ReadOnly Disposition\r\n" + e.Message.ToString(), System.Diagnostics.EventLogEntryType.Error); | ||||
|                 EventLogDMO.Add(new WinEventLog() { IssueID = issueID, UserID = @User.Identity.Name, DocumentType = "Lot Disposition", OperationType = "Error", Comments = exceptionString }); | ||||
|                 throw new Exception(e.Message); | ||||
|  | ||||
|             } | ||||
|             return Redirect(mrbUrl); | ||||
|         } | ||||
|  | ||||
|         // | ||||
|  | ||||
| @ -154,6 +154,10 @@ namespace Fab2ApprovalSystem.DMO | ||||
|             FabApprovalTrainingEntities db = new FabApprovalTrainingEntities(); | ||||
|             var TrainingData = from a in db.TrainingAssignments where a.TrainingID == TrainingID select a; | ||||
|  | ||||
|             RefreshTrainingData(TrainingID, TrainingData); | ||||
|  | ||||
|             TrainingData = from a in db.TrainingAssignments where a.TrainingID == TrainingID select a; | ||||
|  | ||||
|             return TrainingData.ToList(); | ||||
|         } | ||||
|         public List<TrainingAssignment> GetTrainingAssignments(int TrainingID) | ||||
| @ -161,6 +165,10 @@ namespace Fab2ApprovalSystem.DMO | ||||
|             FabApprovalTrainingEntities db = new FabApprovalTrainingEntities(); | ||||
|             var TrainingData = from a in db.TrainingAssignments where a.TrainingID == TrainingID && a.Deleted != true select a; | ||||
|  | ||||
|             RefreshTrainingData(TrainingID, TrainingData); | ||||
|  | ||||
|             TrainingData = from a in db.TrainingAssignments where a.TrainingID == TrainingID select a; | ||||
|  | ||||
|             return TrainingData.ToList(); | ||||
|         } | ||||
|         public List<TrainingAssignment> GetTrainingAssignmentsByUser(int TrainingID, int userID) | ||||
| @ -452,5 +460,20 @@ namespace Fab2ApprovalSystem.DMO | ||||
|             return openAssignments; | ||||
|         } | ||||
|  | ||||
|         private void RefreshTrainingData(int TrainingID, IQueryable<TrainingAssignment> TrainingData) | ||||
|         { | ||||
|             bool assignmentIsIncomplete = false; | ||||
|             UserAccountDMO userAccountDMO = new UserAccountDMO(); | ||||
|             foreach (TrainingAssignment assignment in TrainingData) | ||||
|             { | ||||
|                 LoginModel userModel = userAccountDMO.GetUserByID(assignment.UserID); | ||||
|                 if (!userModel.IsActive) UpdateAssignmentStatus(assignment.ID); | ||||
|                 if (assignment.Deleted != true && (assignment.DateCompleted is null || assignment.DateCompleted > DateTime.Now)) | ||||
|                     assignmentIsIncomplete = true; | ||||
|             } | ||||
|  | ||||
|             if (!assignmentIsIncomplete) | ||||
|                 UpdateTrainingStatus(TrainingID); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -14,6 +14,6 @@ namespace Fab2ApprovalSystem.ViewModels | ||||
|         public string Originator { get; set; } | ||||
|         public DateTime? AssignedDate { get; set; } | ||||
|         public DateTime? DueDate { get; set; } | ||||
|         public string pcrMesaID { get; set; } | ||||
|         public string pcrMesaID { get; set; } = string.Empty; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -227,7 +227,7 @@ | ||||
|                             ) | ||||
|                         </div> | ||||
|                         <div class="col-sm-6 col-sm-offset-4" style="color:red;"> | ||||
|                             ECN: Qualtiy and Dept. Specific<br />MRB: Quality, Production, Engineering, OPC<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change) | ||||
|                             ECN: Qualtiy and Dept. Specific<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change) | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|  | ||||
| @ -171,7 +171,7 @@ | ||||
|                             ) | ||||
|                         </div> | ||||
|                         <div class="col-sm-6 col-sm-offset-4" style="color:red;"> | ||||
|                             ECN: Qualtiy and Dept. Specific<br />MRB: Quality, Production, Engineering, OPC<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change) | ||||
|                             ECN: Qualtiy and Dept. Specific<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change) | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|  | ||||
| @ -259,7 +259,7 @@ | ||||
|                             ) | ||||
|                         </div> | ||||
|                         <div class="col-sm-6 col-sm-offset-4" style="color:red;"> | ||||
|                             ECN: Qualtiy and Dept. Specific<br />MRB: Quality, Production, Engineering, OPC<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change) | ||||
|                             ECN: Qualtiy and Dept. Specific<br />PCRB: Quality, Production, Engineering, Dept. Specific (scope of change) | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     <div class="form-group"> | ||||
|  | ||||
| @ -83,14 +83,33 @@ | ||||
|                         @*<li><a href=@Url.Action("Create", "LotDisposition")>Lot Dispostion</a></li>*@ | ||||
|                         @*<li><a href=@Url.Action("Create", "MRB")>Create MRB</a></li>*@ | ||||
|                         <li><a href=@Url.Action("Create", "ECN")>Create ECN/TECN</a></li> | ||||
|                         @if (!string.IsNullOrWhiteSpace(Session["JWT"].ToString())) { | ||||
|                             string jwt = Session["JWT"].ToString(); | ||||
|                             string encodedJwt = System.Net.WebUtility.UrlEncode(jwt); | ||||
|                             string refreshToken = Session["RefreshToken"].ToString(); | ||||
|                             string encodedRefreshToken = System.Net.WebUtility.UrlEncode(refreshToken); | ||||
|                             string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ?? | ||||
|                                 "https://localhost:7255"; | ||||
|                             string mrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/mrb/new"; | ||||
|                             <li><a href="@mrbUrl">Create MRB</a></li> | ||||
|                             @*string pcrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/pcrb/new"; | ||||
|                             <li><a href="@pcrbUrl">Create PCRB</a></li>*@ | ||||
|                         } else { | ||||
|                             string wasmClientUrl = Environment.GetEnvironmentVariable("FabApprovalWasmClientUrl") ?? | ||||
|                                                         "https://localhost:7255"; | ||||
|                             string mrbUrl = wasmClientUrl + "/redirect?redirectPath=/mrb/new"; | ||||
|                             <li><a href="@mrbUrl">Create MRB</a></li> | ||||
|                             @*string pcrbUrl = wasmClientUrl + "/redirect?redirectPath=/pcrb/new"; | ||||
|                             <li><a href="@pcrbUrl">Create PCRB</a></li>*@ | ||||
|                         } | ||||
|                         @*<li><a href=@Url.Action("CreateWorkRequest", "LotTraveler")>Create Special Work Request</a></li>*@ | ||||
|                         @*<li><a href=@Url.Action("Create", "ChangeControl")>Create PCR</a></li>*@ | ||||
|                         <li><a href=@Url.Action("Create", "Audit")>Create Audit</a></li> | ||||
|                         <li><a href=@Url.Action("Create", "CorrectiveAction")>Create Corrective Action</a></li> | ||||
|                         @*@if (Convert.ToBoolean(Session[GlobalVars.CAN_CREATE_PARTS_REQUEST])) | ||||
|                         { | ||||
|                         <li><a href=@Url.Action("Create", "PartsRequest")>Create New/Repair Spare Parts Request</a></li> | ||||
|                         }*@ | ||||
|         { | ||||
|         <li><a href=@Url.Action("Create", "PartsRequest")>Create New/Repair Spare Parts Request</a></li> | ||||
|         }*@ | ||||
|                         @*<li><a href="#">Another Doc</a></li>*@ | ||||
|                     </ul> | ||||
|                 </li> | ||||
| @ -133,6 +152,8 @@ | ||||
|                 "https://localhost:7255"; | ||||
|             string mrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/mrb/all"; | ||||
|             menu.Add().Text("MRB").Url(mrbUrl); | ||||
|             //string pcrbUrl = wasmClientUrl + "/redirect?jwt=" + encodedJwt + "&refreshToken=" + encodedRefreshToken + "&redirectPath=/pcrb/all"; | ||||
|             //menu.Add().Text("PCRB").Url(pcrbUrl); | ||||
|             //menu.Add().Text("Special Work Requests").Action("SpecialWorkRequestList", "Home"); | ||||
|             //menu.Add().Text("PCRB").Action("ChangeControlList", "Home"); | ||||
|             //menu.Add().Text("MRB").Action("MRBList", "Home"); | ||||
|  | ||||
| @ -132,6 +132,11 @@ | ||||
|         <requestLimits maxAllowedContentLength="1073741824" /> | ||||
|       </requestFiltering> | ||||
|     </security> | ||||
|       <httpProtocol> | ||||
|           <customHeaders> | ||||
|               <add name="Access-Control-Allow-Origin" value="*" /> | ||||
|           </customHeaders> | ||||
|       </httpProtocol> | ||||
|   </system.webServer> | ||||
|   <runtime> | ||||
|     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> | ||||
|  | ||||
							
								
								
									
										260
									
								
								MesaFabApproval.API/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								MesaFabApproval.API/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,260 @@ | ||||
| # Remove the line below if you want to inherit .editorconfig settings from higher directories | ||||
| root = true | ||||
|  | ||||
| # C# files | ||||
| [*.cs] | ||||
|  | ||||
| #### Core EditorConfig Options #### | ||||
|  | ||||
| # Indentation and spacing | ||||
| indent_size = 4 | ||||
| indent_style = space | ||||
| tab_width = 4 | ||||
|  | ||||
| # New line preferences | ||||
| end_of_line = crlf | ||||
| insert_final_newline = false | ||||
|  | ||||
| #### .NET Coding Conventions #### | ||||
|  | ||||
| # Organize usings | ||||
| dotnet_separate_import_directive_groups = true | ||||
| dotnet_sort_system_directives_first = true | ||||
| file_header_template = unset | ||||
|  | ||||
| # this. and Me. preferences | ||||
| dotnet_style_qualification_for_event = true | ||||
| dotnet_style_qualification_for_field = true | ||||
| dotnet_style_qualification_for_method = true | ||||
| dotnet_style_qualification_for_property = true | ||||
|  | ||||
| # Language keywords vs BCL types preferences | ||||
| dotnet_style_predefined_type_for_locals_parameters_members = true | ||||
| dotnet_style_predefined_type_for_member_access = true | ||||
|  | ||||
| # Parentheses preferences | ||||
| dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:error | ||||
| dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:error | ||||
| dotnet_style_parentheses_in_other_operators = never_if_unnecessary | ||||
| dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:error | ||||
|  | ||||
| # Modifier preferences | ||||
| dotnet_style_require_accessibility_modifiers = for_non_interface_members | ||||
|  | ||||
| # Expression-level preferences | ||||
| dotnet_style_coalesce_expression = false | ||||
| dotnet_style_collection_initializer = true | ||||
| dotnet_style_explicit_tuple_names = true:error | ||||
| dotnet_style_namespace_match_folder = true | ||||
| dotnet_style_null_propagation = true | ||||
| dotnet_style_object_initializer = true | ||||
| dotnet_style_operator_placement_when_wrapping = beginning_of_line | ||||
| dotnet_style_prefer_auto_properties = true | ||||
| dotnet_style_prefer_compound_assignment = true | ||||
| dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion | ||||
| dotnet_style_prefer_conditional_expression_over_return = true:suggestion | ||||
| dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed | ||||
| dotnet_style_prefer_inferred_anonymous_type_member_names = false | ||||
| dotnet_style_prefer_inferred_tuple_names = false | ||||
| dotnet_style_prefer_is_null_check_over_reference_equality_method = true | ||||
| dotnet_style_prefer_simplified_boolean_expressions = true | ||||
| dotnet_style_prefer_simplified_interpolation = true | ||||
|  | ||||
| # Field preferences | ||||
| dotnet_style_readonly_field = true:warning | ||||
|  | ||||
| # Parameter preferences | ||||
| dotnet_code_quality_unused_parameters = all:error | ||||
|  | ||||
| # Suppression preferences | ||||
| dotnet_remove_unnecessary_suppression_exclusions = none | ||||
|  | ||||
| # New line preferences | ||||
| dotnet_style_allow_multiple_blank_lines_experimental = false:error | ||||
| dotnet_style_allow_statement_immediately_after_block_experimental = false:warning | ||||
|  | ||||
| #### C# Coding Conventions #### | ||||
|  | ||||
| # var preferences | ||||
| csharp_style_var_elsewhere = false:error | ||||
| csharp_style_var_for_built_in_types = false:error | ||||
| csharp_style_var_when_type_is_apparent = false:error | ||||
|  | ||||
| # Expression-bodied members | ||||
| csharp_style_expression_bodied_accessors = false | ||||
| csharp_style_expression_bodied_constructors = false | ||||
| csharp_style_expression_bodied_indexers = false | ||||
| csharp_style_expression_bodied_lambdas = true | ||||
| csharp_style_expression_bodied_local_functions = false | ||||
| csharp_style_expression_bodied_methods = when_on_single_line:suggestion | ||||
| csharp_style_expression_bodied_operators = false | ||||
| csharp_style_expression_bodied_properties = false | ||||
|  | ||||
| # Pattern matching preferences | ||||
| csharp_style_pattern_matching_over_as_with_null_check = false | ||||
| csharp_style_pattern_matching_over_is_with_cast_check = false | ||||
| csharp_style_prefer_extended_property_pattern = true | ||||
| csharp_style_prefer_not_pattern = true | ||||
| csharp_style_prefer_pattern_matching = true | ||||
| csharp_style_prefer_switch_expression = false | ||||
|  | ||||
| # Null-checking preferences | ||||
| csharp_style_conditional_delegate_call = false | ||||
|  | ||||
| # Modifier preferences | ||||
| csharp_prefer_static_local_function = false | ||||
| csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async | ||||
| csharp_style_prefer_readonly_struct = true:warning | ||||
| csharp_style_prefer_readonly_struct_member = true | ||||
|  | ||||
| # Code-block preferences | ||||
| csharp_prefer_braces = when_multiline:error | ||||
| csharp_prefer_simple_using_statement = false | ||||
| csharp_style_namespace_declarations = file_scoped:error | ||||
| csharp_style_prefer_method_group_conversion = true:suggestion | ||||
| csharp_style_prefer_top_level_statements = true:error | ||||
|  | ||||
| # Expression-level preferences | ||||
| csharp_prefer_simple_default_expression = true | ||||
| csharp_style_deconstructed_variable_declaration = false | ||||
| csharp_style_implicit_object_creation_when_type_is_apparent = false | ||||
| csharp_style_inlined_variable_declaration = true | ||||
| csharp_style_prefer_index_operator = false:error | ||||
| csharp_style_prefer_local_over_anonymous_function = true:error | ||||
| csharp_style_prefer_null_check_over_type_check = true | ||||
| csharp_style_prefer_range_operator = false:error | ||||
| csharp_style_prefer_tuple_swap = true | ||||
| csharp_style_prefer_utf8_string_literals = true | ||||
| csharp_style_throw_expression = false | ||||
| csharp_style_unused_value_assignment_preference = unused_local_variable | ||||
| csharp_style_unused_value_expression_statement_preference = unused_local_variable | ||||
|  | ||||
| # 'using' directive preferences | ||||
| csharp_using_directive_placement = outside_namespace:error | ||||
|  | ||||
| # New line preferences | ||||
| csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true | ||||
| csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true | ||||
| csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true | ||||
| csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:error | ||||
| csharp_style_allow_embedded_statements_on_same_line_experimental = true | ||||
|  | ||||
| #### C# Formatting Rules #### | ||||
|  | ||||
| # New line preferences | ||||
| csharp_new_line_before_catch = false | ||||
| csharp_new_line_before_else = false | ||||
| csharp_new_line_before_finally = false | ||||
| csharp_new_line_before_members_in_anonymous_types = true | ||||
| csharp_new_line_before_members_in_object_initializers = true | ||||
| csharp_new_line_before_open_brace = none | ||||
| csharp_new_line_between_query_expression_clauses = true | ||||
|  | ||||
| # Indentation preferences | ||||
| csharp_indent_block_contents = true | ||||
| csharp_indent_braces = false | ||||
| csharp_indent_case_contents = true | ||||
| csharp_indent_case_contents_when_block = true | ||||
| csharp_indent_labels = one_less_than_current | ||||
| csharp_indent_switch_labels = true | ||||
|  | ||||
| # Space preferences | ||||
| csharp_space_after_cast = false | ||||
| csharp_space_after_colon_in_inheritance_clause = true | ||||
| csharp_space_after_comma = true | ||||
| csharp_space_after_dot = false | ||||
| csharp_space_after_keywords_in_control_flow_statements = true | ||||
| csharp_space_after_semicolon_in_for_statement = true | ||||
| csharp_space_around_binary_operators = before_and_after | ||||
| csharp_space_around_declaration_statements = false | ||||
| csharp_space_before_colon_in_inheritance_clause = true | ||||
| csharp_space_before_comma = false | ||||
| csharp_space_before_dot = false | ||||
| csharp_space_before_open_square_brackets = false | ||||
| csharp_space_before_semicolon_in_for_statement = false | ||||
| csharp_space_between_empty_square_brackets = false | ||||
| csharp_space_between_method_call_empty_parameter_list_parentheses = false | ||||
| csharp_space_between_method_call_name_and_opening_parenthesis = false | ||||
| csharp_space_between_method_call_parameter_list_parentheses = false | ||||
| csharp_space_between_method_declaration_empty_parameter_list_parentheses = false | ||||
| csharp_space_between_method_declaration_name_and_open_parenthesis = false | ||||
| csharp_space_between_method_declaration_parameter_list_parentheses = false | ||||
| csharp_space_between_parentheses = false | ||||
| csharp_space_between_square_brackets = false | ||||
|  | ||||
| # Wrapping preferences | ||||
| csharp_preserve_single_line_blocks = true | ||||
| csharp_preserve_single_line_statements = true | ||||
|  | ||||
| #### Naming styles #### | ||||
|  | ||||
| # Naming rules | ||||
|  | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i | ||||
|  | ||||
| dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.types_should_be_pascal_case.symbols = types | ||||
| dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| # Symbol specifications | ||||
|  | ||||
| dotnet_naming_symbols.interface.applicable_kinds = interface | ||||
| dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.interface.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum | ||||
| dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.types.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method | ||||
| dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.non_field_members.required_modifiers =  | ||||
|  | ||||
| # Naming styles | ||||
|  | ||||
| dotnet_naming_style.pascal_case.required_prefix =  | ||||
| dotnet_naming_style.pascal_case.required_suffix =  | ||||
| dotnet_naming_style.pascal_case.word_separator =  | ||||
| dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.begins_with_i.required_prefix = I | ||||
| dotnet_naming_style.begins_with_i.required_suffix =  | ||||
| dotnet_naming_style.begins_with_i.word_separator =  | ||||
| dotnet_naming_style.begins_with_i.capitalization = pascal_case | ||||
|  | ||||
| # Question | ||||
|  | ||||
| dotnet_diagnostic.ASP0025.severity = none # Use AddAuthorizationBuilder to register authorization services and construct policies [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1510.severity = none # Use 'ArgumentNullException.ThrowIfNull' instead of explicitly throwing a new exception instance [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1806.severity = none # 'MRBNumberIsValid' calls 'Append' but does not use the value the method returns. Linq methods are known to not have side effects. Use the result in a conditional statement, assign the result to a variable, or pass it as an argument to another method. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1822.severity = none # Member 'GenerateRefreshToken' does not access instance data and can be marked as static [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1825.severity = none # Avoid unnecessary zero-length array allocations.  Use Array.Empty<string>() instead. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1827.severity = none # Count() is used where Any() could be used instead to improve performance [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1829.severity = none # Use the \"Count\" property instead of Enumerable.Count() [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1861.severity = none # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA2208.severity = none # Method UpdateMRBAction passes 'MRB action cannot be null' as the paramName argument to a ArgumentNullException constructor. Replace this argument with one of the method's parameter names. Note that the provided parameter name should have the exact casing as declared on the method. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA2254.severity = none # The type or namespace name 'AspNetCore' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?) [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.CS0234.severity = none # The type or namespace name 'Extensions' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?) [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.CS0246.severity = none # The type or namespace name 'ILogger<>' could not be found (are you missing a using directive or an assembly reference?) [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.CS1998.severity = none # This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CS8019.severity = none # Unnecessary using directive | ||||
| dotnet_diagnostic.CS8600.severity = none # Converting null literal or possible null value to non-nullable type. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CS8625.severity = none # Cannot convert null literal to non-nullable reference type. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CS8765.severity = none # Nullability of type of parameter 'obj' doesn't match overridden member (possibly because of nullability attributes). [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.IDE0001.severity = none # Name can be simplified | ||||
| dotnet_diagnostic.IDE0017.severity = none # Object initialization can be simplified [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0022.severity = none # Use expression body for method [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.IDE0028.severity = none # Collection initialization can be simplified [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0044.severity = none # Make field readonly [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0046.severity = none # "'if' statement can be simplified [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0052.severity = none # Private member 'CustomerController._cache' can be removed as the value assigned to it is never read [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0074.severity = none # Use compound assignment [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0305.severity = none # Collection initialization can be simplified [MesaFabApproval.API]", | ||||
| dotnet_style_prefer_conditional_expression_over_return = false | ||||
							
								
								
									
										1
									
								
								MesaFabApproval.API/.vscode/format-report.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								MesaFabApproval.API/.vscode/format-report.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| [] | ||||
							
								
								
									
										30
									
								
								MesaFabApproval.API/.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								MesaFabApproval.API/.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| { | ||||
|     // Use IntelliSense to learn about possible attributes. | ||||
|     // Hover to view descriptions of existing attributes. | ||||
|     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|     "version": "0.2.0", | ||||
|     "configurations": [ | ||||
|         { | ||||
|             "name": ".NET Core Launch (console)", | ||||
|             "type": "coreclr", | ||||
|             "request": "launch", | ||||
|             "preLaunchTask": "build", | ||||
|             "program": "${workspaceFolder}/bin/Debug/net8.0/MesaFabApproval.API.dll", | ||||
|             "args": [], | ||||
|             "cwd": "${workspaceFolder}", | ||||
|             "console": "integratedTerminal", | ||||
|             "stopAtEntry": false | ||||
|         }, | ||||
|         { | ||||
|             "name": ".NET Core Attach", | ||||
|             "type": "coreclr", | ||||
|             "request": "attach" | ||||
|         }, | ||||
|         { | ||||
|             "type": "node", | ||||
|             "request": "launch", | ||||
|             "name": "node Launch Current Opened File", | ||||
|             "program": "${file}" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										6
									
								
								MesaFabApproval.API/.vscode/mklink.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								MesaFabApproval.API/.vscode/mklink.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| # mklink | ||||
|  | ||||
| ```bash 1731705389065 = 638673021890650000 = Fri Nov 15 2024 14:16:28 GMT-0700 (Mountain Standard Time) | ||||
| mklink /J "L:\DevOps\Mesa_FI\MesaFabApproval\MesaFabApproval.API\.vscode\.UserSecrets" "C:\Users\phares\AppData\Roaming\Microsoft\UserSecrets\0b98e1f2-95ed-4edd-8149-58cce51ca059" | ||||
| dotnet run --urls=https://localhost:7114/ -C Release | ||||
| ``` | ||||
							
								
								
									
										424
									
								
								MesaFabApproval.API/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										424
									
								
								MesaFabApproval.API/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,424 @@ | ||||
| { | ||||
|   "[markdown]": { | ||||
|     "editor.wordWrap": "off" | ||||
|   }, | ||||
|   "files.exclude": { | ||||
|     "**/.git": false, | ||||
|     "**/node_modules": true | ||||
|   }, | ||||
|   "files.watcherExclude": { | ||||
|     "**/node_modules": true | ||||
|   }, | ||||
|   "cSpell.words": [ | ||||
|     "abutton", | ||||
|     "accessibilities", | ||||
|     "accodingly", | ||||
|     "acknowledgmentby", | ||||
|     "Acks", | ||||
|     "actionsheet", | ||||
|     "Additonal", | ||||
|     "Addtional", | ||||
|     "againm", | ||||
|     "agendaview", | ||||
|     "Antlr", | ||||
|     "Appover", | ||||
|     "Appprrovers", | ||||
|     "Approvalog", | ||||
|     "Aprovers", | ||||
|     "Aproverslist", | ||||
|     "asax", | ||||
|     "aspnetmvc", | ||||
|     "Assignedto", | ||||
|     "Atachments", | ||||
|     "Attachemnt", | ||||
|     "Attachemnts", | ||||
|     "Attchment", | ||||
|     "auditee", | ||||
|     "Auditee", | ||||
|     "Auditees", | ||||
|     "automaically", | ||||
|     "Autthorized", | ||||
|     "beacuase", | ||||
|     "beforeunload", | ||||
|     "Belguim", | ||||
|     "bfound", | ||||
|     "bgcolor", | ||||
|     "Bies", | ||||
|     "binded", | ||||
|     "blackbackground", | ||||
|     "blackhover", | ||||
|     "blackpressed", | ||||
|     "blueenergy", | ||||
|     "blueopal", | ||||
|     "buttongroup", | ||||
|     "BYMRB", | ||||
|     "bytesgot", | ||||
|     "CAID", | ||||
|     "casection", | ||||
|     "CAXXXX", | ||||
|     "CCPCR", | ||||
|     "CCPCRB", | ||||
|     "cellspacing", | ||||
|     "Cheeso's", | ||||
|     "chkbx", | ||||
|     "Clib", | ||||
|     "colorpicker", | ||||
|     "columnmenu", | ||||
|     "columnsreorder", | ||||
|     "columnsresize", | ||||
|     "comming", | ||||
|     "Containmen", | ||||
|     "Copmments", | ||||
|     "correctiv", | ||||
|     "Correctivet", | ||||
|     "Creat", | ||||
|     "currentd", | ||||
|     "Cyle", | ||||
|     "dadada", | ||||
|     "darkbluehover", | ||||
|     "darkbluepressed", | ||||
|     "darkred", | ||||
|     "datafields", | ||||
|     "datasource", | ||||
|     "dataviz", | ||||
|     "datepicker", | ||||
|     "datetimepicker", | ||||
|     "dayview", | ||||
|     "Deletet", | ||||
|     "Delgation", | ||||
|     "DENITED", | ||||
|     "Deparmtent", | ||||
|     "departmentids", | ||||
|     "Descirption", | ||||
|     "devprog", | ||||
|     "dfeffc", | ||||
|     "Disp", | ||||
|     "Dispo", | ||||
|     "Dispoitio", | ||||
|     "Dispos", | ||||
|     "Dispositon", | ||||
|     "Dispostion", | ||||
|     "Dispostions", | ||||
|     "dispotypevalidation", | ||||
|     "Docbase", | ||||
|     "Documentum", | ||||
|     "Documetum", | ||||
|     "dont", | ||||
|     "downlaoded", | ||||
|     "draganddrop", | ||||
|     "dragcancel", | ||||
|     "dropdownlist", | ||||
|     "Eamils", | ||||
|     "ECNPCRB", | ||||
|     "Ecns", | ||||
|     "edmx", | ||||
|     "EECN", | ||||
|     "emai", | ||||
|     "emailparams", | ||||
|     "Emergrncy", | ||||
|     "energyblue", | ||||
|     "Eran", | ||||
|     "Esql", | ||||
|     "ETECN", | ||||
|     "EXCELOPENXML", | ||||
|     "existinglocation", | ||||
|     "Expando", | ||||
|     "extrafield", | ||||
|     "fadc", | ||||
|     "fbec", | ||||
|     "fcfdfd", | ||||
|     "fdff", | ||||
|     "fece", | ||||
|     "feeebd", | ||||
|     "ffdc", | ||||
|     "ffdd", | ||||
|     "fieldset", | ||||
|     "FILIPE", | ||||
|     "filtermenu", | ||||
|     "Fldr", | ||||
|     "flintstone", | ||||
|     "FLOWLOCS", | ||||
|     "FMEA", | ||||
|     "ftplib", | ||||
|     "FTPSPN", | ||||
|     "GETDATE", | ||||
|     "gitea", | ||||
|     "globaldocudms", | ||||
|     "glyphicons", | ||||
|     "groupable", | ||||
|     "Guids", | ||||
|     "halflings", | ||||
|     "Hexsize", | ||||
|     "highcontrast", | ||||
|     "Hmac", | ||||
|     "holdsteps", | ||||
|     "hostspecific", | ||||
|     "icenium", | ||||
|     "IECN", | ||||
|     "imagebrowser", | ||||
|     "IMRB", | ||||
|     "Infineon", | ||||
|     "Insertd", | ||||
|     "inverseicons", | ||||
|     "IPCRB", | ||||
|     "ISADMIN", | ||||
|     "islast", | ||||
|     "ISNULL", | ||||
|     "ITAR", | ||||
|     "jquery", | ||||
|     "jqueryval", | ||||
|     "jqwidgets", | ||||
|     "jqxbuttongroup", | ||||
|     "jqxbuttons", | ||||
|     "jqxcalendar", | ||||
|     "jqxchart", | ||||
|     "jqxcheckbox", | ||||
|     "jqxcolorpicker", | ||||
|     "jqxcombobox", | ||||
|     "jqxcore", | ||||
|     "jqxdata", | ||||
|     "jqxdatatable", | ||||
|     "jqxdatetimeinput", | ||||
|     "jqxdocking", | ||||
|     "jqxdockpanel", | ||||
|     "jqxdragdrop", | ||||
|     "jqxdropdownbutton", | ||||
|     "jqxdropdownlist", | ||||
|     "jqxexpander", | ||||
|     "jqxgauge", | ||||
|     "jqxgrid", | ||||
|     "jqxinput", | ||||
|     "jqxknockout", | ||||
|     "jqxlistbox", | ||||
|     "jqxlistmenu", | ||||
|     "jqxmaskedinput", | ||||
|     "jqxmenu", | ||||
|     "jqxnavigationbar", | ||||
|     "jqxnumberinput", | ||||
|     "jqxpanel", | ||||
|     "jqxpasswordinput", | ||||
|     "jqxprogressbar", | ||||
|     "jqxradiobutton", | ||||
|     "jqxrangeselector", | ||||
|     "jqxrating", | ||||
|     "jqxresponse", | ||||
|     "jqxscrollbar", | ||||
|     "jqxscrollview", | ||||
|     "jqxslider", | ||||
|     "jqxsplitter", | ||||
|     "jqxswitchbutton", | ||||
|     "jqxtabs", | ||||
|     "jqxtooltip", | ||||
|     "jqxtouch", | ||||
|     "jqxtree", | ||||
|     "jqxtreegrid", | ||||
|     "jqxtreemap", | ||||
|     "jqxvalidator", | ||||
|     "jqxwindow", | ||||
|     "kendogridcustom", | ||||
|     "kendoui", | ||||
|     "labelelement", | ||||
|     "labelledby", | ||||
|     "Leanred", | ||||
|     "lightgray", | ||||
|     "linkbutton", | ||||
|     "Linq", | ||||
|     "Listdiv", | ||||
|     "listview", | ||||
|     "Lnks", | ||||
|     "localfilename", | ||||
|     "loclist", | ||||
|     "logis", | ||||
|     "logtext", | ||||
|     "loopmis", | ||||
|     "lotdispo", | ||||
|     "LOTDISPSITION", | ||||
|     "Lotfile", | ||||
|     "lotlist", | ||||
|     "lotstatusoption", | ||||
|     "LTRIM", | ||||
|     "MADUREIRA", | ||||
|     "mailrelay", | ||||
|     "MDTM", | ||||
|     "meego", | ||||
|     "meetingid", | ||||
|     "menubutton", | ||||
|     "mesafi", | ||||
|     "metroblack", | ||||
|     "metrodark", | ||||
|     "miliseconds", | ||||
|     "modalview", | ||||
|     "modernizr", | ||||
|     "Modernizr", | ||||
|     "monthview", | ||||
|     "MRBIs", | ||||
|     "Mrbs", | ||||
|     "msecs", | ||||
|     "multipleextended", | ||||
|     "Navigatable", | ||||
|     "nbsp", | ||||
|     "newbase", | ||||
|     "newchange", | ||||
|     "newdi", | ||||
|     "newfilename", | ||||
|     "newsource", | ||||
|     "Newtonsoft", | ||||
|     "notications", | ||||
|     "Notifcation", | ||||
|     "Notifyf", | ||||
|     "NTLM", | ||||
|     "Nullcc", | ||||
|     "numerictextbox", | ||||
|     "objdata", | ||||
|     "OCAP", | ||||
|     "occured", | ||||
|     "odata", | ||||
|     "oldfilename", | ||||
|     "OLHOLD", | ||||
|     "onclick", | ||||
|     "onmousemove", | ||||
|     "OPDESC", | ||||
|     "OPENQUERY", | ||||
|     "Oper", | ||||
|     "operationslist", | ||||
|     "Orginator", | ||||
|     "Originatorname", | ||||
|     "Ouellette", | ||||
|     "Owin", | ||||
|     "pageable", | ||||
|     "Pageable", | ||||
|     "panelbar", | ||||
|     "parentid", | ||||
|     "parminput", | ||||
|     "parms", | ||||
|     "Parms", | ||||
|     "particula", | ||||
|     "pasv", | ||||
|     "PASV", | ||||
|     "PATINDEX", | ||||
|     "PCRB", | ||||
|     "PCRBID", | ||||
|     "pcrvalues", | ||||
|     "pdbonly", | ||||
|     "Preventitive", | ||||
|     "preventivet", | ||||
|     "Prevetative", | ||||
|     "proces", | ||||
|     "Processedl", | ||||
|     "procs", | ||||
|     "productfamilies", | ||||
|     "progess", | ||||
|     "progressbar", | ||||
|     "qrcode", | ||||
|     "Quanityt", | ||||
|     "rangebar", | ||||
|     "Recep", | ||||
|     "Recepient", | ||||
|     "recieved", | ||||
|     "recordlock", | ||||
|     "remotefilename", | ||||
|     "reorderable", | ||||
|     "reportform", | ||||
|     "reportslist", | ||||
|     "reportslistdiv", | ||||
|     "Reqquired", | ||||
|     "Reqs", | ||||
|     "Requiest", | ||||
|     "Responsibles", | ||||
|     "RETR", | ||||
|     "Revisioing", | ||||
|     "Revisioned", | ||||
|     "Revison", | ||||
|     "rgba", | ||||
|     "rkotian", | ||||
|     "RNFR", | ||||
|     "RNTO", | ||||
|     "Roless", | ||||
|     "roundbg", | ||||
|     "RTRIM", | ||||
|     "SAMDB", | ||||
|     "scroller", | ||||
|     "scrollview", | ||||
|     "seleced", | ||||
|     "selectionlog", | ||||
|     "Selectpart", | ||||
|     "sess", | ||||
|     "Sfisharepoint", | ||||
|     "shinyblack", | ||||
|     "showpassword", | ||||
|     "SIGNON", | ||||
|     "simpleparser", | ||||
|     "slddrw", | ||||
|     "sldprt", | ||||
|     "sortasc", | ||||
|     "sortascbutton", | ||||
|     "sortdesc", | ||||
|     "sortdescbutton", | ||||
|     "sortremove", | ||||
|     "sparkline", | ||||
|     "splitview", | ||||
|     "SPNMRB", | ||||
|     "SPNPDB", | ||||
|     "SSRS", | ||||
|     "Sssign", | ||||
|     "Staus", | ||||
|     "stylesheet", | ||||
|     "Submited", | ||||
|     "subrole", | ||||
|     "subroles", | ||||
|     "Succefully", | ||||
|     "Succesfully", | ||||
|     "sucessfully", | ||||
|     "SURP", | ||||
|     "Swashbuckle", | ||||
|     "SWRN", | ||||
|     "tabindex", | ||||
|     "tabstrip", | ||||
|     "Tahoma", | ||||
|     "taskcompleted", | ||||
|     "Tasklist", | ||||
|     "Taveler", | ||||
|     "TECN", | ||||
|     "TECNs", | ||||
|     "TEMIRWAP", | ||||
|     "tempecd", | ||||
|     "tempimplement", | ||||
|     "templabel", | ||||
|     "tempvalue", | ||||
|     "TEMSA", | ||||
|     "timepicker", | ||||
|     "Tobe", | ||||
|     "Toplevel", | ||||
|     "Totrav", | ||||
|     "trainingby", | ||||
|     "Traininglist", | ||||
|     "traininglistdiv", | ||||
|     "transanction", | ||||
|     "Trav", | ||||
|     "Traveller", | ||||
|     "Traverler", | ||||
|     "TRAVLELER", | ||||
|     "Travler", | ||||
|     "TREEVIEW", | ||||
|     "trigerred", | ||||
|     "ttinclude", | ||||
|     "Uhandled", | ||||
|     "Updat", | ||||
|     "Uplaod", | ||||
|     "Upto", | ||||
|     "userevents", | ||||
|     "userids", | ||||
|     "userlist", | ||||
|     "Validatable", | ||||
|     "valueelement", | ||||
|     "Variabls", | ||||
|     "Verdana", | ||||
|     "vgrid", | ||||
|     "viewmodel", | ||||
|     "vsdoc", | ||||
|     "whethere", | ||||
|     "windowsphone", | ||||
|     "Winsock", | ||||
|     "worlflow" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										135
									
								
								MesaFabApproval.API/.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								MesaFabApproval.API/.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,135 @@ | ||||
| { | ||||
|     "version": "2.0.0", | ||||
|     "tasks": [ | ||||
|         { | ||||
|             "label": "User Secrets Init", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API.csproj", | ||||
|                 "init" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "User Secrets Set", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API.csproj", | ||||
|                 "set", | ||||
|                 "_UserSecretsId", | ||||
|                 "0b98e1f2-95ed-4edd-8149-58cce51ca059" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Format", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "--report", | ||||
|                 ".vscode", | ||||
|                 "--verbosity", | ||||
|                 "detailed", | ||||
|                 "--severity", | ||||
|                 "warn" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Format-Whitespaces", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "whitespace" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "build", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "testDebug", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "testRelease", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary", | ||||
|                 "-c", | ||||
|                 "Release" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "publish", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "watch", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "watch", | ||||
|                 "run", | ||||
|                 "--project", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Publish AOT", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "-r", | ||||
|                 "win-x64", | ||||
|                 "-c", | ||||
|                 "Release", | ||||
|                 "-p:PublishAot=true", | ||||
|                 "${workspaceFolder}/MesaFabApproval.API.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -7,7 +7,7 @@ public interface ISmtpClientWrapper { | ||||
| } | ||||
|  | ||||
| public class SmtpClientWrapper : ISmtpClientWrapper { | ||||
|     private SmtpClient _client; | ||||
|     private readonly SmtpClient _client; | ||||
|  | ||||
|     public SmtpClientWrapper(SmtpClient client) { | ||||
|         _client = client ?? | ||||
| @ -17,4 +17,4 @@ public class SmtpClientWrapper : ISmtpClientWrapper { | ||||
|     public void Send(MailMessage message) { | ||||
|         _client.Send(message); | ||||
|     } | ||||
| } | ||||
| } | ||||
| @ -398,4 +398,4 @@ public class ApprovalController : ControllerBase { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
| @ -1,4 +1,5 @@ | ||||
| using System.Security.Authentication; | ||||
| using System.Security.Principal; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Services; | ||||
| @ -11,6 +12,7 @@ using Microsoft.AspNetCore.Mvc; | ||||
| namespace MesaFabApproval.API.Controllers; | ||||
|  | ||||
| [ApiController] | ||||
| [AllowAnonymous] | ||||
| public class AuthenticationController : ControllerBase { | ||||
|     private readonly ILogger<AuthenticationController> _logger; | ||||
|     private readonly IMonInWorkerClient _monInClient; | ||||
| @ -21,12 +23,11 @@ public class AuthenticationController : ControllerBase { | ||||
|                                     IAuthenticationService authenticationService) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected"); | ||||
|         _authenticationService = authenticationService ??  | ||||
|         _authenticationService = authenticationService ?? | ||||
|             throw new ArgumentNullException("IAuthenticationService not injected"); | ||||
|     } | ||||
|  | ||||
|     [HttpPost] | ||||
|     [AllowAnonymous] | ||||
|     [Route("auth/login")] | ||||
|     public async Task<IActionResult> Login(AuthAttempt login) { | ||||
|         DateTime start = DateTime.Now; | ||||
| @ -72,7 +73,51 @@ public class AuthenticationController : ControllerBase { | ||||
|     } | ||||
|  | ||||
|     [HttpPost] | ||||
|     [AllowAnonymous] | ||||
|     [Route("auth/login/localWindows")] | ||||
|     public async Task<IActionResult> LoginLocalWindows(WindowsIdentity identity) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isArgumentError = false; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to perform local Windows authentication"); | ||||
|  | ||||
|             if (identity is null) throw new ArgumentNullException("identity cannot be null"); | ||||
|  | ||||
|             LoginResult loginResult = await _authenticationService.AttemptLocalUserAuth(identity); | ||||
|  | ||||
|             if (loginResult.IsAuthenticated) | ||||
|                 return Ok(loginResult); | ||||
|  | ||||
|             return Unauthorized(); | ||||
|         } catch (ArgumentException ex) { | ||||
|             isArgumentError = true; | ||||
|             errorMessage = $"Invalid argument. {ex.Message}"; | ||||
|             return BadRequest(errorMessage); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Cannot authenticate local Windows user, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "LoginLocalWindows"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isArgumentError) { | ||||
|                 _logger.LogWarning(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } else if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpPost] | ||||
|     [Route("auth/refresh")] | ||||
|     public async Task<IActionResult> Refresh(AuthAttempt authAttempt) { | ||||
|         DateTime start = DateTime.Now; | ||||
| @ -117,4 +162,17 @@ public class AuthenticationController : ControllerBase { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     [HttpOptions] | ||||
|     [Route("auth/refresh")] | ||||
|     public IActionResult RefreshOptions() { | ||||
|         try { | ||||
|             _logger.LogInformation("Auth refresh options"); | ||||
|  | ||||
|             return Ok(); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Error in auth refresh options. Exception: {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										56
									
								
								MesaFabApproval.API/Controllers/CAController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								MesaFabApproval.API/Controllers/CAController.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| using MesaFabApproval.API.Services; | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Services; | ||||
|  | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
|  | ||||
| namespace MesaFabApproval.API.Controllers; | ||||
| [ApiController] | ||||
| [Authorize] | ||||
| public class CAController : ControllerBase { | ||||
|     private readonly ILogger<ApprovalController> _logger; | ||||
|     private readonly ICAService _caService; | ||||
|     private readonly IMonInWorkerClient _monInClient; | ||||
|  | ||||
|     public CAController(ILogger<ApprovalController> logger, ICAService caService, | ||||
|                               IMonInWorkerClient monInClient) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _caService = caService ?? throw new ArgumentNullException("ICAService not injected"); | ||||
|         _monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected"); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("ca/isValidCANumber")] | ||||
|     public async Task<IActionResult> IsValidCANumber(int number) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to determine if {number} is a valid CA#"); | ||||
|  | ||||
|             if (number <= 0) return Ok(false); | ||||
|  | ||||
|             bool isValid = await _caService.IsValidCANumber(number); | ||||
|  | ||||
|             return Ok(isValid); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Cannot determine if {number} is a valid CA#, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "IsValidCANumber"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										57
									
								
								MesaFabApproval.API/Controllers/CustomerController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								MesaFabApproval.API/Controllers/CustomerController.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | ||||
| using MesaFabApproval.API.Services; | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Services; | ||||
|  | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| namespace MesaFabApproval.API.Controllers; | ||||
|  | ||||
| [ApiController] | ||||
| [Authorize] | ||||
| public class CustomerController : ControllerBase { | ||||
|     private readonly ILogger<CustomerController> _logger; | ||||
|     private readonly IMonInWorkerClient _monInClient; | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly ICustomerService _customerService; | ||||
|  | ||||
|     public CustomerController(ILogger<CustomerController> logger, IMonInWorkerClient monInClient, IMemoryCache cache, ICustomerService customerService) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected"); | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _customerService = customerService ?? throw new ArgumentNullException("ICustomerService"); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("customer/all")] | ||||
|     public async Task<IActionResult> GetAllCustomerNames() { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get all customer names"); | ||||
|  | ||||
|             IEnumerable<string> customerNames = await _customerService.GetCustomerNames(); | ||||
|  | ||||
|             return Ok(customerNames); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Cannot get user by LoginID, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "GetAllCustomerNames"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										56
									
								
								MesaFabApproval.API/Controllers/ECNController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								MesaFabApproval.API/Controllers/ECNController.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| using MesaFabApproval.API.Services; | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Services; | ||||
|  | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
|  | ||||
| namespace MesaFabApproval.API.Controllers; | ||||
| [ApiController] | ||||
| [Authorize] | ||||
| public class ECNController : ControllerBase { | ||||
|     private readonly ILogger<ApprovalController> _logger; | ||||
|     private readonly IECNService _ecnService; | ||||
|     private readonly IMonInWorkerClient _monInClient; | ||||
|  | ||||
|     public ECNController(ILogger<ApprovalController> logger, IECNService ecnService, | ||||
|                               IMonInWorkerClient monInClient) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _ecnService = ecnService ?? throw new ArgumentNullException("IECNService not injected"); | ||||
|         _monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected"); | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("ecn/isValidEcnNumber")] | ||||
|     public async Task<IActionResult> IsValidEcnNumber(int number) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to determine if {number} is a valid ECN#"); | ||||
|  | ||||
|             if (number <= 0) return Ok(false); | ||||
|  | ||||
|             bool isValid = await _ecnService.IsValidECNNumber(number); | ||||
|  | ||||
|             return Ok(isValid); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Cannot determine if {number} is a valid ECN#, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "IsValidEcnNumber"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,5 @@ | ||||
| using MesaFabApproval.API.Services; | ||||
| using MesaFabApproval.Models; | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Services; | ||||
|  | ||||
| @ -15,10 +16,16 @@ public class MRBController : ControllerBase { | ||||
|     private readonly IMRBService _mrbService; | ||||
|     private readonly IMonInWorkerClient _monInClient; | ||||
|  | ||||
|     public MRBController(ILogger<MRBController> logger, IMRBService mrbService, IMonInWorkerClient monInClient) { | ||||
|     private readonly string _mrbAttachmentPath; | ||||
|  | ||||
|     public MRBController(ILogger<MRBController> logger, | ||||
|                          IMRBService mrbService, | ||||
|                          IMonInWorkerClient monInClient, | ||||
|                          AppSettings appSettings) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _mrbService = mrbService ?? throw new ArgumentNullException("IMRBService not injected"); | ||||
|         _monInClient = monInClient ?? throw new ArgumentNullException("IMonInWorkerClient not injected"); | ||||
|         _mrbAttachmentPath = appSettings.MrbAttachmentPath; | ||||
|     } | ||||
|  | ||||
|     [HttpPost] | ||||
| @ -63,9 +70,51 @@ public class MRBController : ControllerBase { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpDelete] | ||||
|     [Route("mrb/delete")] | ||||
|     public async Task<IActionResult> DeleteMRB(int mrbNumber) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isArgumentError = false; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to delete MRB# {mrbNumber}"); | ||||
|  | ||||
|             if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number"); | ||||
|  | ||||
|             await _mrbService.DeleteMRB(mrbNumber); | ||||
|  | ||||
|             return Ok(); | ||||
|         } catch (ArgumentException ex) { | ||||
|             isArgumentError = true; | ||||
|             errorMessage = ex.Message; | ||||
|             return BadRequest(errorMessage); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Cannot delete MRB {mrbNumber}, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "DeleteMRB"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isArgumentError) { | ||||
|                 _logger.LogWarning(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } else if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("mrb/all")] | ||||
|     public async Task<IActionResult> GetAllMRBs() { | ||||
|     public async Task<IActionResult> GetAllMRBs(bool bypassCache) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
| @ -73,7 +122,7 @@ public class MRBController : ControllerBase { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get all MRBs"); | ||||
|  | ||||
|             IEnumerable<MRB> allMrbs = await _mrbService.GetAllMRBs(); | ||||
|             IEnumerable<MRB> allMrbs = await _mrbService.GetAllMRBs(bypassCache); | ||||
|  | ||||
|             return Ok(allMrbs); | ||||
|         } catch (Exception ex) { | ||||
| @ -95,9 +144,41 @@ public class MRBController : ControllerBase { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("mrb/numberIsValid")] | ||||
|     public async Task<IActionResult> NumberIsValid(int number) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to determine if {number} is a valid MRB#"); | ||||
|  | ||||
|             bool isValid = await _mrbService.MRBNumberIsValid(number); | ||||
|  | ||||
|             return Ok(isValid); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Unable to determine if {number} is a valid MRB#, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "IsValidMRBNumber"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("mrb/getById")] | ||||
|     public async Task<IActionResult> GetMRBById(int id) { | ||||
|     public async Task<IActionResult> GetMRBById(int id, bool bypassCache = false) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isArgumentError = false; | ||||
|         bool isInternalError = false; | ||||
| @ -108,7 +189,7 @@ public class MRBController : ControllerBase { | ||||
|  | ||||
|             if (id <= 0) throw new ArgumentException("Invalid MRB number"); | ||||
|  | ||||
|             MRB mrb = await _mrbService.GetMRBById(id); | ||||
|             MRB mrb = await _mrbService.GetMRBById(id, bypassCache); | ||||
|  | ||||
|             return Ok(mrb); | ||||
|         } catch (ArgumentException ex) { | ||||
| @ -148,7 +229,7 @@ public class MRBController : ControllerBase { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get an MRB by Title"); | ||||
|  | ||||
|             if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or emtpy"); | ||||
|             if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty"); | ||||
|  | ||||
|             MRB mrb = await _mrbService.GetMRBByTitle(title, bypassCache); | ||||
|  | ||||
| @ -432,6 +513,49 @@ public class MRBController : ControllerBase { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpGet] | ||||
|     [Route("mrb/action/attachments")] | ||||
|     public async Task<IActionResult> GetActionAttachmentsForMRB(int mrbNumber, bool bypassCache) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isArgumentError = false; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to get action attachments for MRB {mrbNumber}"); | ||||
|  | ||||
|             if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#"); | ||||
|  | ||||
|             List<MRBActionAttachment> attachments = | ||||
|                 (await _mrbService.GetAllActionAttachmentsForMRB(mrbNumber, bypassCache)).ToList(); | ||||
|  | ||||
|             return Ok(attachments); | ||||
|         } catch (ArgumentException ex) { | ||||
|             isArgumentError = true; | ||||
|             errorMessage = ex.Message; | ||||
|             return BadRequest(errorMessage); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Cannot get action attachments for MRB# {mrbNumber}, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "GetMRBActionAttachments"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isArgumentError) { | ||||
|                 _logger.LogWarning(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } else if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpPost] | ||||
|     [Route("mrb/attach")] | ||||
|     public async Task<IActionResult> SaveMRBAttachment([FromForm] IEnumerable<IFormFile> files, int mrbNumber) { | ||||
| @ -476,6 +600,50 @@ public class MRBController : ControllerBase { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpPost] | ||||
|     [Route("mrb/action/attach")] | ||||
|     public async Task<IActionResult> SaveMRBActionAttachment([FromForm] IEnumerable<IFormFile> files, int actionId) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isArgumentError = false; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to save MRB action attachments"); | ||||
|  | ||||
|             if (files is null) throw new ArgumentNullException("Files cannot be null"); | ||||
|             if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty"); | ||||
|             if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID"); | ||||
|  | ||||
|             IEnumerable<UploadResult> uploadResults = (await _mrbService.UploadActionAttachments(files, actionId)).ToList(); | ||||
|  | ||||
|             return Ok(uploadResults); | ||||
|         } catch (ArgumentException ex) { | ||||
|             isArgumentError = true; | ||||
|             errorMessage = ex.Message; | ||||
|             return BadRequest(errorMessage); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Cannot save MRB action attachments, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "SaveMRBActionAttachments"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isArgumentError) { | ||||
|                 _logger.LogWarning(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } else if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [AllowAnonymous] | ||||
|     [HttpGet] | ||||
|     [Route("mrb/attachmentFile")] | ||||
| @ -531,6 +699,64 @@ public class MRBController : ControllerBase { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [AllowAnonymous] | ||||
|     [HttpGet] | ||||
|     [Route("mrb/actions/csvFile")] | ||||
|     public async Task<IActionResult> GetMRBActionsCsvFile(int mrbNumber) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isArgumentError = false; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get MRB actions CSC file"); | ||||
|  | ||||
|             if (!(await _mrbService.MRBNumberIsValid(mrbNumber))) | ||||
|                 throw new ArgumentException($"{mrbNumber} is not a valid MRB#"); | ||||
|  | ||||
|             string path = $"{_mrbAttachmentPath}\\{mrbNumber}\\mrb{mrbNumber}Actions.csv"; | ||||
|  | ||||
|             await _mrbService.ConvertActionsToCsvFile(mrbNumber, path); | ||||
|  | ||||
|             byte[] fs = System.IO.File.ReadAllBytes(path); | ||||
|  | ||||
|             const string defaultContentType = "application/octet-stream"; | ||||
|  | ||||
|             FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider(); | ||||
|  | ||||
|             if (!contentTypeProvider.TryGetContentType(path, out string? contentType)) { | ||||
|                 contentType = defaultContentType; | ||||
|             } | ||||
|  | ||||
|             return new FileContentResult(fs, contentType) { | ||||
|                 FileDownloadName = $"mrb{mrbNumber}Actions.csv" | ||||
|             }; | ||||
|         } catch (ArgumentException ex) { | ||||
|             isArgumentError = true; | ||||
|             errorMessage = ex.Message; | ||||
|             return BadRequest(errorMessage); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Cannot get MRB actions CSC file, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "GetMRBActionsCSVFile"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isArgumentError) { | ||||
|                 _logger.LogWarning(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } else if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [HttpDelete] | ||||
|     [Route("mrb/attach")] | ||||
|     public async Task<IActionResult> DeleteMRBAttachment(MRBAttachment attachment) { | ||||
| @ -702,4 +928,48 @@ public class MRBController : ControllerBase { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     [HttpPost] | ||||
|     [Route("mrb/notify/qa-pre-approver")] | ||||
|     public async Task<IActionResult> NotifyQAPreApprover(MRBNotification notification) { | ||||
|         DateTime start = DateTime.Now; | ||||
|         bool isArgumentError = false; | ||||
|         bool isInternalError = false; | ||||
|         string errorMessage = ""; | ||||
|  | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to notify QA pre approver"); | ||||
|  | ||||
|             if (notification is null) throw new ArgumentNullException("MRBNotification cannot be null"); | ||||
|             if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null"); | ||||
|             if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("Message cannot be null or empty"); | ||||
|  | ||||
|             await _mrbService.NotifyQAPreApprover(notification); | ||||
|  | ||||
|             return Ok(); | ||||
|         } catch (ArgumentException ex) { | ||||
|             isArgumentError = true; | ||||
|             errorMessage = ex.Message; | ||||
|             return BadRequest(errorMessage); | ||||
|         } catch (Exception ex) { | ||||
|             isInternalError = true; | ||||
|             errorMessage = $"Unable to notify QA pre approver, because {ex.Message}"; | ||||
|             return Problem(errorMessage); | ||||
|         } finally { | ||||
|             string metricName = "NotifyQAPreApprover"; | ||||
|             DateTime end = DateTime.Now; | ||||
|             double millisecondsDiff = (end - start).TotalMilliseconds; | ||||
|             _monInClient.PostAverage(metricName + "Latency", millisecondsDiff); | ||||
|  | ||||
|             if (isArgumentError) { | ||||
|                 _logger.LogWarning(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } else if (isInternalError) { | ||||
|                 _logger.LogError(errorMessage); | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Critical); | ||||
|             } else { | ||||
|                 _monInClient.PostStatus(metricName, StatusValue.Ok); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1126
									
								
								MesaFabApproval.API/Controllers/PCRBController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1126
									
								
								MesaFabApproval.API/Controllers/PCRBController.cs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -220,4 +220,4 @@ public class UserController : ControllerBase { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
							
								
								
									
										9
									
								
								MesaFabApproval.API/GlobalSuppressions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								MesaFabApproval.API/GlobalSuppressions.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| // This file is used by Code Analysis to maintain SuppressMessage | ||||
| // attributes that are applied to this project. | ||||
| // Project-level suppressions either have no target or are given | ||||
| // a specific target and scoped to a namespace, type, member, etc. | ||||
|  | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
|  | ||||
| [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>", Scope = "member", Target = "~M:MesaFabApprovalAPI.Services.AuthenticationService.AuthenticateUser(MesaFabApproval.Shared.Models.AuthAttempt)~System.Threading.Tasks.Task{MesaFabApproval.Shared.Models.LoginResult}")] | ||||
| [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>", Scope = "member", Target = "~M:MesaFabApprovalAPI.Services.AuthenticationService.AttemptLocalUserAuth(System.Security.Principal.WindowsIdentity)~System.Threading.Tasks.Task{MesaFabApproval.Shared.Models.LoginResult}")] | ||||
| @ -1,32 +1,28 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Web"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>net8.0</TargetFramework> | ||||
|     <Nullable>enable</Nullable> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|     <UserSecretsId>0b98e1f2-95ed-4edd-8149-58cce51ca059</UserSecretsId> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Dapper" Version="2.1.35" /> | ||||
|     <PackageReference Include="Dapper.Contrib" Version="2.0.78" /> | ||||
|     <PackageReference Include="dotenv.net" Version="3.1.3" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.5" /> | ||||
|     <PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.0" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" /> | ||||
|     <PackageReference Include="NLog" Version="5.3.2" /> | ||||
|     <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.11" /> | ||||
|     <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> | ||||
|     <PackageReference Include="dotenv.net" Version="3.2.0" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" /> | ||||
|     <PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.1" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" /> | ||||
|     <PackageReference Include="NLog" Version="5.2.8" /> | ||||
|     <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.8" /> | ||||
|     <PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.0" /> | ||||
|     <PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.0" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\MesaFabApproval.Shared\MesaFabApproval.Shared.csproj" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Reference Include="System.DirectoryServices.AccountManagement"> | ||||
|       <HintPath>..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.DirectoryServices.AccountManagement.dll</HintPath> | ||||
|     </Reference> | ||||
|   </ItemGroup> | ||||
|  | ||||
| </Project> | ||||
| </Project> | ||||
							
								
								
									
										93
									
								
								MesaFabApproval.API/Models/AppSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								MesaFabApproval.API/Models/AppSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| using System; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace MesaFabApproval.Models; | ||||
|  | ||||
| public record AppSettings(string Company, | ||||
|                           string DbConnectionString, | ||||
|                           string JwtAudience, | ||||
|                           string JwtIssuer, | ||||
|                           string JwtKey, | ||||
|                           string MrbAttachmentPath, | ||||
|                           string PcrbAttachmentPath, | ||||
|                           bool ShouldSendEmail, | ||||
|                           string SiteBaseUrl, | ||||
|                           string WorkingDirectoryName) { | ||||
|  | ||||
|     public override string ToString() { | ||||
|         string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     public static AppSettings Get(IConfigurationRoot configurationRoot) { | ||||
|         AppSettings? result; | ||||
|         try { | ||||
| #pragma warning disable IL3050, IL2026 | ||||
|             result = configurationRoot.Get<AppSettings>() ?? throw new Exception(); | ||||
| #pragma warning restore IL3050, IL2026 | ||||
|         } catch (Exception) { | ||||
|             List<string> paths = []; | ||||
|             foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) { | ||||
|                 if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) | ||||
|                     continue; | ||||
|                 if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) | ||||
|                     continue; | ||||
|                 paths.Add(physicalFileProvider.Root); | ||||
|             } | ||||
|  | ||||
|             throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}"); | ||||
|         } | ||||
|  | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     internal static void SetEnvironmentVariables(WebApplicationBuilder builder) { | ||||
|         builder.Configuration.AddUserSecrets<Program>(); | ||||
|         AppSettings appSettings = Get(builder.Configuration); | ||||
|         Environment.SetEnvironmentVariable("FabApprovalDbConnectionString", appSettings.DbConnectionString); | ||||
|         Environment.SetEnvironmentVariable("FabApprovalJwtAudience", appSettings.JwtAudience); | ||||
|         Environment.SetEnvironmentVariable("FabApprovalJwtIssuer", appSettings.JwtIssuer); | ||||
|         Environment.SetEnvironmentVariable("FabApprovalJwtKey", appSettings.JwtKey); | ||||
|         Environment.SetEnvironmentVariable("FabApprovalMrbAttachmentPath", appSettings.MrbAttachmentPath); | ||||
|         Environment.SetEnvironmentVariable("FabApprovalPcrbAttachmentPath", appSettings.PcrbAttachmentPath); | ||||
|         Environment.SetEnvironmentVariable("FabApprovalShouldSendEmail", appSettings.ShouldSendEmail.ToString()); | ||||
|         Environment.SetEnvironmentVariable("NewFabApprovalBaseUrl", appSettings.SiteBaseUrl); | ||||
|     } | ||||
|  | ||||
|     internal static AppSettings LoadEnvironmentVariables() { | ||||
|         AppSettings result; | ||||
|         string dbConnectionString = Environment.GetEnvironmentVariable("FabApprovalDbConnectionString") ?? | ||||
|             throw new ArgumentNullException("FabApprovalDbConnectionString environment variable not found"); | ||||
|         string jwtAudience = Environment.GetEnvironmentVariable("FabApprovalJwtAudience") ?? | ||||
|             throw new ArgumentNullException("FabApprovalJwtAudience environment variable not found"); | ||||
|         string jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer") ?? | ||||
|             throw new ArgumentNullException("FabApprovalJwtIssuer environment variable not found"); | ||||
|         string jwtKey = Environment.GetEnvironmentVariable("FabApprovalJwtKey") ?? | ||||
|             throw new ArgumentNullException("FabApprovalJwtKey environment variable not found"); | ||||
|         string mrbAttachmentPath = Environment.GetEnvironmentVariable("FabApprovalMrbAttachmentPath") ?? | ||||
|             throw new ArgumentNullException("FabApprovalMrbAttachmentPath environment variable not found"); | ||||
|         string pcrbAttachmentPath = Environment.GetEnvironmentVariable("FabApprovalPcrbAttachmentPath") ?? | ||||
|             throw new ArgumentNullException("FabApprovalPcrbAttachmentPath environment variable not found"); | ||||
|         if (!bool.TryParse(Environment.GetEnvironmentVariable("FabApprovalShouldSendEmail"), out bool shouldSendEmail)) | ||||
|             throw new ArgumentNullException("FabApprovalShouldSendEmail environment variable not found"); | ||||
|         string siteBaseUrl = Environment.GetEnvironmentVariable("NewFabApprovalBaseUrl") ?? | ||||
|             throw new ArgumentNullException("FabApprovalBaseUrl environment variable not found"); | ||||
|         result = new("Infineon Technologies Americas Corp.", | ||||
|                      DbConnectionString: dbConnectionString, | ||||
|                      JwtAudience: jwtAudience, | ||||
|                      JwtIssuer: jwtIssuer, | ||||
|                      JwtKey: jwtKey, | ||||
|                      MrbAttachmentPath: mrbAttachmentPath, | ||||
|                      PcrbAttachmentPath: pcrbAttachmentPath, | ||||
|                      ShouldSendEmail: shouldSendEmail, | ||||
|                      SiteBaseUrl: siteBaseUrl, | ||||
|                      "IFXApps"); | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
|  | ||||
| [JsonSourceGenerationOptions(WriteIndented = true)] | ||||
| [JsonSerializable(typeof(AppSettings))] | ||||
| internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext { | ||||
| } | ||||
| @ -1,16 +1,24 @@ | ||||
| using MesaFabApproval.Shared.Services; | ||||
| using NLog.Web; | ||||
| using MesaFabApprovalAPI.Services; | ||||
| using Microsoft.OpenApi.Models; | ||||
| using dotenv.net; | ||||
| using Microsoft.AspNetCore.Authentication.JwtBearer; | ||||
| using Microsoft.IdentityModel.Tokens; | ||||
| using System.Text; | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using MesaFabApproval.API.Services; | ||||
| using NLog.Extensions.Logging; | ||||
| using MesaFabApproval.API.Clients; | ||||
| using System.Diagnostics; | ||||
| using System.Net.Mail; | ||||
| using System.Text; | ||||
|  | ||||
| using dotenv.net; | ||||
|  | ||||
| using MesaFabApproval.API.Clients; | ||||
| using MesaFabApproval.API.Services; | ||||
| using MesaFabApproval.Models; | ||||
| using MesaFabApproval.Shared.Services; | ||||
|  | ||||
| using MesaFabApprovalAPI.Services; | ||||
|  | ||||
| using Microsoft.AspNetCore.Authentication.JwtBearer; | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Microsoft.AspNetCore.HttpLogging; | ||||
| using Microsoft.IdentityModel.Tokens; | ||||
| using Microsoft.OpenApi.Models; | ||||
|  | ||||
| using NLog.Extensions.Logging; | ||||
| using NLog.Web; | ||||
|  | ||||
| WebApplicationBuilder builder = WebApplication.CreateBuilder(args); | ||||
|  | ||||
| @ -20,27 +28,39 @@ builder.Logging.ClearProviders(); | ||||
| builder.Logging.SetMinimumLevel(LogLevel.Trace); | ||||
| builder.Logging.AddNLog(); | ||||
|  | ||||
| /*builder.Services.AddHttpLogging(o => { | ||||
|     o.LoggingFields = HttpLoggingFields.All; | ||||
|     o.RequestHeaders.Add("bearer"); | ||||
|     o.MediaTypeOptions.AddText("application/javascript"); | ||||
|     o.RequestBodyLogLimit = 4096; | ||||
|     o.ResponseBodyLogLimit = 4096; | ||||
|     o.CombineLogs = true; | ||||
| });*/ | ||||
|  | ||||
| builder.Services.AddMemoryCache(); | ||||
|  | ||||
| string jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer") ?? | ||||
|     throw new ArgumentNullException("FabApprovalJwtIssuer environment variable not found"); | ||||
| string jwtAudience = Environment.GetEnvironmentVariable("FabApprovalJwtAudience") ?? | ||||
|     throw new ArgumentNullException("FabApprovalJwtAudience environment variable not found"); | ||||
| string jwtKey = Environment.GetEnvironmentVariable("FabApprovalJwtKey") ?? | ||||
|     throw new ArgumentNullException("FabApprovalJwtKey environment variable not found"); | ||||
| if (Debugger.IsAttached) { | ||||
|     string? jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer"); | ||||
|     if (string.IsNullOrEmpty(jwtIssuer)) { | ||||
|         AppSettings.SetEnvironmentVariables(builder); | ||||
|     } | ||||
| } | ||||
|  | ||||
| builder.Services.AddAuthentication(options => {  | ||||
| AppSettings appSettings = AppSettings.LoadEnvironmentVariables(); | ||||
| builder.Services.AddSingleton(_ => appSettings); | ||||
|  | ||||
| builder.Services.AddAuthentication(options => { | ||||
|     options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; | ||||
|     options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; | ||||
| }) | ||||
| .AddJwtBearer(options => {  | ||||
| .AddJwtBearer(options => { | ||||
|     options.RequireHttpsMetadata = false; | ||||
|     options.SaveToken = true; | ||||
|     options.TokenValidationParameters = new TokenValidationParameters { | ||||
|         ValidateIssuerSigningKey = true, | ||||
|         ValidIssuer = jwtIssuer, | ||||
|         ValidIssuer = appSettings.JwtIssuer, | ||||
|         ValidateAudience = false, | ||||
|         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)), | ||||
|         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(appSettings.JwtKey)), | ||||
|         ClockSkew = TimeSpan.Zero | ||||
|     }; | ||||
| }); | ||||
| @ -54,21 +74,34 @@ builder.Services.AddAuthorization(options => { | ||||
|  | ||||
| builder.Services.AddHttpClient(); | ||||
|  | ||||
| builder.Services.AddCors(options => { | ||||
|     options.AddDefaultPolicy(options => { | ||||
|         options.AllowAnyOrigin(); | ||||
|         options.AllowAnyMethod(); | ||||
|         options.AllowAnyHeader(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| builder.Services.AddScoped<IDbConnectionService, DbConnectionService>(); | ||||
| builder.Services.AddScoped<IDalService, DalService>(); | ||||
| builder.Services.AddScoped<SmtpClient>((serviceProvider) => { | ||||
|     return new SmtpClient("mailrelay-external.infineon.com"); | ||||
| }); | ||||
| builder.Services.AddScoped<ISmtpClientWrapper, SmtpClientWrapper>(); | ||||
| builder.Services.AddScoped<ICustomerService, CustomerService>(); | ||||
| builder.Services.AddScoped<ICAService, CAService>(); | ||||
| builder.Services.AddScoped<IECNService, ECNService>(); | ||||
| builder.Services.AddScoped<ISmtpService, SmtpService>(); | ||||
| builder.Services.AddScoped<IUserService, UserService>(); | ||||
| builder.Services.AddScoped<IMonInWorkerClient, MonInWorkerClient>(); | ||||
| builder.Services.AddScoped<IAuthenticationService, AuthenticationService>(); | ||||
| builder.Services.AddScoped<IMRBService, MRBService>(); | ||||
| builder.Services.AddScoped<IPCRBService, PCRBService>(); | ||||
| builder.Services.AddScoped<IApprovalService, ApprovalService>(); | ||||
|  | ||||
| builder.Services.AddControllers(); | ||||
|  | ||||
| #if DEBUG | ||||
| builder.Services.AddEndpointsApiExplorer(); | ||||
| builder.Services.AddSwaggerGen(c => { | ||||
|     c.SwaggerDoc("v1", new OpenApiInfo { | ||||
| @ -95,28 +128,22 @@ builder.Services.AddSwaggerGen(c => { | ||||
|     } | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| builder.Services.AddCors(options => { | ||||
|     options.AddDefaultPolicy( | ||||
|         builder => { | ||||
|             builder.AllowAnyOrigin() | ||||
|                    .AllowAnyHeader() | ||||
|                    .AllowAnyMethod(); | ||||
|         }); | ||||
| }); | ||||
| #endif | ||||
|  | ||||
| WebApplication app = builder.Build(); | ||||
|  | ||||
| if (Debugger.IsAttached) | ||||
|     app.Services.GetRequiredService<IApprovalService>(); | ||||
|  | ||||
| app.UseCors(); | ||||
|  | ||||
| // Configure the HTTP request pipeline. | ||||
| if (app.Environment.IsDevelopment()) | ||||
| { | ||||
| if (app.Environment.IsDevelopment()) { | ||||
|     app.UseSwagger(); | ||||
|     app.UseSwaggerUI(); | ||||
| } | ||||
|  | ||||
| app.UseHttpsRedirection(); | ||||
| // app.UseHttpLogging(); | ||||
|  | ||||
| app.UseAuthentication(); | ||||
|  | ||||
| @ -125,3 +152,6 @@ app.UseAuthorization(); | ||||
| app.MapControllers(); | ||||
|  | ||||
| app.Run(); | ||||
|  | ||||
| NLog.LogManager.Flush(); | ||||
| NLog.LogManager.Shutdown(); | ||||
| @ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. | ||||
| --> | ||||
| <Project> | ||||
|   <PropertyGroup> | ||||
|     <DeleteExistingFiles>false</DeleteExistingFiles> | ||||
|     <DeleteExistingFiles>true</DeleteExistingFiles> | ||||
|     <ExcludeApp_Data>false</ExcludeApp_Data> | ||||
|     <LaunchSiteAfterPublish>true</LaunchSiteAfterPublish> | ||||
|     <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration> | ||||
| @ -18,5 +18,6 @@ https://go.microsoft.com/fwlink/?LinkID=208121. | ||||
|     <RuntimeIdentifier>win-x64</RuntimeIdentifier> | ||||
|     <ProjectGuid>852e528d-015a-43b5-999d-f281e3359e5e</ProjectGuid> | ||||
|     <SelfContained>true</SelfContained> | ||||
|     <PublishReadyToRun>true</PublishReadyToRun> | ||||
|   </PropertyGroup> | ||||
| </Project> | ||||
| @ -1,7 +1,7 @@ | ||||
| using System.Net.Mail; | ||||
| using System.Text; | ||||
| using System.Text; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Utilities; | ||||
|  | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| @ -42,9 +42,9 @@ public class ApprovalService : IApprovalService { | ||||
|             queryBuilder.Append("insert into Approval (IssueID, RoleName, SubRole, UserID, SubRoleID, ItemStatus, "); | ||||
|             queryBuilder.Append("AssignedDate, DocumentTypeID, DisplayDeniedDocument, Step, TaskID) "); | ||||
|             queryBuilder.Append($"values ({approval.IssueID}, '{approval.RoleName}', '{approval.SubRole}', {approval.UserID}, "); | ||||
|             queryBuilder.Append($"{approval.SubRoleID}, 0, '{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"{approval.SubRoleID}, 0, '{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"3, 0, {approval.Step}, {approval.TaskID});"); | ||||
|              | ||||
|  | ||||
|             int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsCreated <= 0) throw new Exception("Unable to insert approval in database"); | ||||
| @ -63,7 +63,7 @@ public class ApprovalService : IApprovalService { | ||||
|             if (issueId <= 0) throw new ArgumentException($"{issueId} is not a valid issue ID"); | ||||
|  | ||||
|             IEnumerable<Approval>? approvals = new List<Approval>(); | ||||
|              | ||||
|  | ||||
|             if (!bypassCache) | ||||
|                 approvals = _cache.Get<IEnumerable<Approval>>($"approvals{issueId}"); | ||||
|  | ||||
| @ -78,7 +78,7 @@ public class ApprovalService : IApprovalService { | ||||
|  | ||||
|                 foreach (Approval approval in approvals) { | ||||
|                     int successfulUpdates = 0; | ||||
|                      | ||||
|  | ||||
|                     User? user = await _userService.GetUserByUserId(approval.UserID); | ||||
|                     if (user is not null) { | ||||
|                         approval.User = user; | ||||
| @ -86,11 +86,11 @@ public class ApprovalService : IApprovalService { | ||||
|                     } | ||||
|  | ||||
|                     if (approval.ItemStatus < 0) | ||||
|                             approval.StatusMessage = "Denied"; | ||||
|                         approval.StatusMessage = "Denied"; | ||||
|                     if (approval.ItemStatus == 0) | ||||
|                             approval.StatusMessage = "Assigned"; | ||||
|                         approval.StatusMessage = "Assigned"; | ||||
|                     if (approval.ItemStatus > 0) | ||||
|                             approval.StatusMessage = "Approved"; | ||||
|                         approval.StatusMessage = "Approved"; | ||||
|                 } | ||||
|  | ||||
|                 _cache.Set($"approvals{issueId}", approvals, DateTimeOffset.Now.AddMinutes(5)); | ||||
| @ -107,7 +107,7 @@ public class ApprovalService : IApprovalService { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to get role ID by name"); | ||||
|  | ||||
|             if (string.IsNullOrWhiteSpace(roleName))  | ||||
|             if (string.IsNullOrWhiteSpace(roleName)) | ||||
|                 throw new ArgumentException("Role name cannot be null or empty"); | ||||
|  | ||||
|             int roleId = _cache.Get<int>($"role{roleName}"); | ||||
| @ -145,7 +145,7 @@ public class ApprovalService : IApprovalService { | ||||
|                 StringBuilder queryBuilder = new(); | ||||
|                 queryBuilder.Append("select src.SubRoleCategoryID, sr.SubRole as SubRoleName, src.SubRoleCategoryItem, sr.SubRoleID "); | ||||
|                 queryBuilder.Append("from SubRole sr join SubRoleCategory src on sr.SubRoleCategoryID=src.SubRoleCategoryID "); | ||||
|                 queryBuilder.Append($"where sr.RoleID={roleId} and sr.SubRole='{subRoleName}'"); | ||||
|                 queryBuilder.Append($"where sr.RoleID={roleId} and sr.SubRole='{subRoleName}' and sr.Inactive=0"); | ||||
|  | ||||
|                 subRoles = (await _dalService.QueryAsync<SubRole>(queryBuilder.ToString())).ToList(); | ||||
|  | ||||
| @ -170,7 +170,7 @@ public class ApprovalService : IApprovalService { | ||||
|             if (subRoleId <= 0) throw new ArgumentException($"{subRoleId} is not a valid sub role ID"); | ||||
|  | ||||
|             List<User>? members = _cache.Get<List<User>>($"approvalMembers{subRoleId}"); | ||||
|              | ||||
|  | ||||
|             if (members is null || members.Count() <= 0) { | ||||
|                 IEnumerable<int>? memberIds = _cache.Get<IEnumerable<int>>($"approvalMemberIds{subRoleId}"); | ||||
|  | ||||
| @ -182,7 +182,7 @@ public class ApprovalService : IApprovalService { | ||||
|                     if (memberIds is null || memberIds.Count() <= 0) | ||||
|                         throw new Exception($"No members found in sub role {subRoleId}"); | ||||
|  | ||||
|                     _cache.Set($"approvalMemberIds{subRoleId}", memberIds, DateTimeOffset.Now.AddHours(1)); | ||||
|                     _cache.Set($"approvalMemberIds{subRoleId}", memberIds, DateTimeOffset.Now.AddMinutes(5)); | ||||
|                 } | ||||
|  | ||||
|                 members = new(); | ||||
| @ -194,7 +194,7 @@ public class ApprovalService : IApprovalService { | ||||
|  | ||||
|                 if (members.Count() <= 0) throw new Exception("No users found with IDs matching those found in SubRole"); | ||||
|  | ||||
|                 _cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddHours(1)); | ||||
|                 _cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddMinutes(5)); | ||||
|             } | ||||
|  | ||||
|             return members; | ||||
| @ -211,7 +211,7 @@ public class ApprovalService : IApprovalService { | ||||
|             if (userId <= 0) throw new ArgumentException($"{userId} is not a valid user ID"); | ||||
|  | ||||
|             IEnumerable<Approval>? approvals = null; | ||||
|              | ||||
|  | ||||
|             if (!bypassCache) approvals = _cache.Get<IEnumerable<Approval>>($"approvalMembers{userId}"); | ||||
|  | ||||
|             if (approvals is null) { | ||||
| @ -246,12 +246,10 @@ public class ApprovalService : IApprovalService { | ||||
|             queryBuilder.Append($"update Approval set IssueID={approval.IssueID}, RoleName='{approval.RoleName}', "); | ||||
|             queryBuilder.Append($"SubRole='{approval.SubRole}', UserID={approval.UserID}, SubRoleID={approval.SubRoleID}, "); | ||||
|             queryBuilder.Append($"ItemStatus={Convert.ToInt32(approval.ItemStatus)}, Step={approval.Step}, "); | ||||
|             if (approval.NotifyDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (approval.CompletedDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"CompletedDate='{approval.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"Comments='{approval.Comments}', "); | ||||
|             queryBuilder.Append($"CompletedDate='{approval.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"Comments='{approval.Comments.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"TaskID={approval.TaskID} "); | ||||
|             queryBuilder.Append($"where ApprovalID={approval.ApprovalID};"); | ||||
|  | ||||
| @ -274,8 +272,9 @@ public class ApprovalService : IApprovalService { | ||||
|             queryBuilder.Append($"update Approval set IssueID={approval.IssueID}, RoleName='{approval.RoleName}', "); | ||||
|             queryBuilder.Append($"SubRole='{approval.SubRole}', UserID={approval.UserID}, SubRoleID={approval.SubRoleID}, "); | ||||
|             queryBuilder.Append($"ItemStatus=1, Step={approval.Step}, "); | ||||
|             if (approval.NotifyDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (approval.NotifyDate < DateTimeUtilities.MIN_DT) | ||||
|                 approval.NotifyDate = DateTimeUtilities.MIN_DT; | ||||
|             queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"CompletedDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"Comments='{approval.Comments}', "); | ||||
| @ -301,8 +300,9 @@ public class ApprovalService : IApprovalService { | ||||
|             queryBuilder.Append($"update Approval set IssueID={approval.IssueID}, RoleName='{approval.RoleName}', "); | ||||
|             queryBuilder.Append($"SubRole='{approval.SubRole}', UserID={approval.UserID}, SubRoleID={approval.SubRoleID}, "); | ||||
|             queryBuilder.Append($"ItemStatus=-1, Step={approval.Step}, "); | ||||
|             if (approval.NotifyDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (approval.NotifyDate < DateTimeUtilities.MIN_DT) | ||||
|                 approval.NotifyDate = DateTimeUtilities.MIN_DT; | ||||
|             queryBuilder.Append($"NotifyDate='{approval.NotifyDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"AssignedDate='{approval.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"CompletedDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"Comments='{approval.Comments}', "); | ||||
| @ -317,4 +317,4 @@ public class ApprovalService : IApprovalService { | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
| @ -3,9 +3,11 @@ using System.IdentityModel.Tokens.Jwt; | ||||
| using System.Security.Authentication; | ||||
| using System.Security.Claims; | ||||
| using System.Security.Cryptography; | ||||
| using System.Security.Principal; | ||||
| using System.Text; | ||||
|  | ||||
| using MesaFabApproval.API.Services; | ||||
| using MesaFabApproval.Models; | ||||
| using MesaFabApproval.Shared.Models; | ||||
|  | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
| @ -15,6 +17,7 @@ namespace MesaFabApprovalAPI.Services; | ||||
|  | ||||
| public interface IAuthenticationService { | ||||
|     public Task<LoginResult> AuthenticateUser(AuthAttempt login); | ||||
|     public Task<LoginResult> AttemptLocalUserAuth(WindowsIdentity identity); | ||||
|     public AuthTokens GenerateAuthTokens(AuthAttempt authAttempt, IEnumerable<string> roles); | ||||
|     public Task<LoginResult> RefreshAuthTokens(AuthAttempt authAttempt); | ||||
| } | ||||
| @ -28,17 +31,13 @@ public class AuthenticationService : IAuthenticationService { | ||||
|     private readonly string _jwtAudience; | ||||
|     private readonly string _jwtKey; | ||||
|  | ||||
|     public AuthenticationService(ILogger<AuthenticationService> logger, IMemoryCache cache, IUserService userService) { | ||||
|     public AuthenticationService(ILogger<AuthenticationService> logger, IMemoryCache cache, IUserService userService, AppSettings appSettings) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _userService = userService ?? throw new ArgumentNullException("IUserService not injected"); | ||||
|  | ||||
|         _jwtIssuer = Environment.GetEnvironmentVariable("FabApprovalJwtIssuer") ?? | ||||
|             throw new ArgumentNullException("FabApprovalJwtIssuer environment variable not found"); | ||||
|         _jwtAudience = Environment.GetEnvironmentVariable("FabApprovalJwtAudience") ?? | ||||
|             throw new ArgumentNullException("FabApprovalJwtAudience environment variable not found"); | ||||
|         _jwtKey = Environment.GetEnvironmentVariable("FabApprovalJwtKey") ?? | ||||
|             throw new ArgumentNullException("FabApprovalJwtKey environment variable not found"); | ||||
|         _jwtKey = appSettings.JwtKey; | ||||
|         _jwtIssuer = appSettings.JwtIssuer; | ||||
|         _jwtAudience = appSettings.JwtAudience; | ||||
|     } | ||||
|  | ||||
|     public async Task<LoginResult> AuthenticateUser(AuthAttempt login) { | ||||
| @ -68,8 +67,6 @@ public class AuthenticationService : IAuthenticationService { | ||||
|  | ||||
|                     AuthTokens tokens = GenerateAuthTokens(login, roles); | ||||
|  | ||||
|                     _cache.Set<string>(login.LoginID, tokens.RefreshToken); | ||||
|  | ||||
|                     return new LoginResult { | ||||
|                         IsAuthenticated = true, | ||||
|                         AuthTokens = tokens, | ||||
| @ -77,8 +74,8 @@ public class AuthenticationService : IAuthenticationService { | ||||
|                     }; | ||||
|                 } else { | ||||
|                     return new LoginResult() { | ||||
|                         IsAuthenticated= false, | ||||
|                         AuthTokens = new() {  | ||||
|                         IsAuthenticated = false, | ||||
|                         AuthTokens = new() { | ||||
|                             JwtToken = "", | ||||
|                             RefreshToken = "" | ||||
|                         }, | ||||
| @ -92,6 +89,36 @@ public class AuthenticationService : IAuthenticationService { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<LoginResult> AttemptLocalUserAuth(WindowsIdentity identity) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to authenticate local Windows system user"); | ||||
|  | ||||
|             if (identity is null) throw new ArgumentNullException("WindowsIdentity cannot be null"); | ||||
|  | ||||
|             User user = await _userService.GetUserByLoginId(identity.Name); | ||||
|  | ||||
|             List<string> roles = new(); | ||||
|  | ||||
|             if (user.IsManager) roles.Add("manager"); | ||||
|             if (user.IsAdmin) roles.Add("admin"); | ||||
|  | ||||
|             AuthAttempt authAttempt = new() { | ||||
|                 LoginID = user.LoginID, | ||||
|             }; | ||||
|  | ||||
|             AuthTokens tokens = GenerateAuthTokens(authAttempt, roles); | ||||
|  | ||||
|             return new LoginResult { | ||||
|                 IsAuthenticated = true, | ||||
|                 AuthTokens = tokens, | ||||
|                 User = user | ||||
|             }; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to authenticate local Windows system user, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public AuthTokens GenerateAuthTokens(AuthAttempt authAttempt, IEnumerable<string> roles) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to generate JWT"); | ||||
| @ -102,7 +129,7 @@ public class AuthenticationService : IAuthenticationService { | ||||
|  | ||||
|             byte[] key = Encoding.ASCII.GetBytes(_jwtKey); | ||||
|  | ||||
|             List<Claim> claims = new() {  | ||||
|             List<Claim> claims = new() { | ||||
|                 new Claim(nameof(authAttempt.LoginID), authAttempt.LoginID) | ||||
|             }; | ||||
|  | ||||
| @ -117,7 +144,7 @@ public class AuthenticationService : IAuthenticationService { | ||||
|                 Audience = _jwtAudience, | ||||
|                 Subject = identity, | ||||
|                 NotBefore = DateTime.Now, | ||||
|                 Expires = DateTime.Now.AddMinutes(5), | ||||
|                 Expires = DateTime.Now.AddHours(8), | ||||
|                 SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) | ||||
|             }; | ||||
|  | ||||
| @ -129,7 +156,19 @@ public class AuthenticationService : IAuthenticationService { | ||||
|  | ||||
|             string refreshToken = GenerateRefreshToken(); | ||||
|  | ||||
|             return new AuthTokens {  | ||||
|             List<string>? refreshTokensForUser = _cache.Get<List<string>>(authAttempt.LoginID); | ||||
|  | ||||
|             if (refreshTokensForUser is null) | ||||
|                 refreshTokensForUser = new List<string>(); | ||||
|  | ||||
|             if (refreshTokensForUser.Count > 9) | ||||
|                 refreshTokensForUser.RemoveRange(9, refreshTokensForUser.Count - 9); | ||||
|  | ||||
|             refreshTokensForUser.Insert(0, refreshToken); | ||||
|  | ||||
|             _cache.Set<List<string>>(authAttempt.LoginID, refreshTokensForUser, DateTimeOffset.Now.AddHours(4)); | ||||
|  | ||||
|             return new AuthTokens { | ||||
|                 JwtToken = jwt, | ||||
|                 RefreshToken = refreshToken | ||||
|             }; | ||||
| @ -164,8 +203,6 @@ public class AuthenticationService : IAuthenticationService { | ||||
|  | ||||
|                 AuthTokens refreshedTokens = GenerateAuthTokens(authAttempt, roles); | ||||
|  | ||||
|                 _cache.Set<string>(authAttempt.LoginID, refreshedTokens.RefreshToken, DateTimeOffset.Now.AddHours(1)); | ||||
|  | ||||
|                 LoginResult loginResult = new LoginResult() { | ||||
|                     IsAuthenticated = true, | ||||
|                     AuthTokens = refreshedTokens, | ||||
| @ -198,16 +235,17 @@ public class AuthenticationService : IAuthenticationService { | ||||
|             if (string.IsNullOrWhiteSpace(refreshToken)) | ||||
|                 throw new ArgumentNullException("Refresh token cannot be null or empty"); | ||||
|  | ||||
|             string? cachedRefreshToken = _cache.Get<string>(loginId); | ||||
|             List<string>? cachedRefreshTokensForUser = _cache.Get<List<string>>(loginId); | ||||
|  | ||||
|             if (cachedRefreshToken is null) return false; | ||||
|             if (cachedRefreshTokensForUser is null || !cachedRefreshTokensForUser.Contains(refreshToken)) { | ||||
|                 _logger.LogInformation($"Could not find cached refresh tokens for user {loginId}"); | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             if (cachedRefreshToken.Equals(refreshToken)) return true; | ||||
|  | ||||
|             return false; | ||||
|             return true; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to validate refresh token. Exception: {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
							
								
								
									
										46
									
								
								MesaFabApproval.API/Services/CAService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								MesaFabApproval.API/Services/CAService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| namespace MesaFabApproval.API.Services; | ||||
|  | ||||
| public interface ICAService { | ||||
|     Task<bool> IsValidCANumber(int number); | ||||
| } | ||||
|  | ||||
| public class CAService : ICAService { | ||||
|     private readonly ILogger<CAService> _logger; | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly IDalService _dalService; | ||||
|  | ||||
|     public CAService(ILogger<CAService> logger, IMemoryCache cache, IDalService dalService) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _dalService = dalService ?? throw new ArgumentNullException("IDalService not injected"); | ||||
|     } | ||||
|  | ||||
|     public async Task<bool> IsValidCANumber(int number) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to determine if {number} is a valid CA#"); | ||||
|  | ||||
|             if (number <= 0) return false; | ||||
|  | ||||
|             IEnumerable<int> caNumbers = _cache.Get<IEnumerable<int>>("caNumbers") ?? new HashSet<int>(); | ||||
|  | ||||
|             if (caNumbers.Contains(number)) return true; | ||||
|  | ||||
|             string sql = $"select count(CANo) as count from _8DCorrectiveAction where CANo={number}"; | ||||
|  | ||||
|             int rowsReturned = (await _dalService.QueryAsync<int>(sql)).FirstOrDefault(); | ||||
|  | ||||
|             if (rowsReturned > 0) { | ||||
|                 caNumbers.Append(number); | ||||
|                 _cache.Set("caNumbers", caNumbers); | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to determine if {number} is a valid CA#, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										38
									
								
								MesaFabApproval.API/Services/CustomerService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								MesaFabApproval.API/Services/CustomerService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| namespace MesaFabApproval.API.Services; | ||||
|  | ||||
| public interface ICustomerService { | ||||
|     Task<IEnumerable<string>> GetCustomerNames(); | ||||
| } | ||||
|  | ||||
| public class CustomerService : ICustomerService { | ||||
|     private readonly ILogger<CustomerService> _logger; | ||||
|     private readonly IDalService _dalService; | ||||
|     private readonly IMemoryCache _cache; | ||||
|  | ||||
|     public CustomerService(ILogger<CustomerService> logger, IDalService dalService, IMemoryCache cache) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _dalService = dalService ?? throw new ArgumentNullException("IDalService not injected"); | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<string>> GetCustomerNames() { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get customer names"); | ||||
|  | ||||
|             IEnumerable<string>? customerNames = _cache.Get<IEnumerable<string>>("allCustomerNames"); | ||||
|  | ||||
|             if (customerNames is null) { | ||||
|                 string sql = "select ProductFamily from ProductFamilies;"; | ||||
|  | ||||
|                 customerNames = (await _dalService.QueryAsync<string>(sql)).ToList(); | ||||
|             } | ||||
|  | ||||
|             return customerNames; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to get customer names, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -86,4 +86,4 @@ public class DalService : IDalService { | ||||
|  | ||||
|         return rowsAffected; | ||||
|     } | ||||
| } | ||||
| } | ||||
| @ -1,5 +1,7 @@ | ||||
| using System.Data; | ||||
|  | ||||
| using MesaFabApproval.Models; | ||||
|  | ||||
| using Microsoft.Data.SqlClient; | ||||
|  | ||||
| namespace MesaFabApproval.API.Services; | ||||
| @ -11,12 +13,10 @@ public interface IDbConnectionService { | ||||
| public class DbConnectionService : IDbConnectionService { | ||||
|     private readonly string _dbConnectionString; | ||||
|  | ||||
|     public DbConnectionService() { | ||||
|         _dbConnectionString = Environment.GetEnvironmentVariable("FabApprovalDbConnectionString") ?? | ||||
|             throw new ArgumentNullException("FabApprovalDbConnectionString environment variable not found"); | ||||
|     public DbConnectionService(AppSettings appSettings) { | ||||
|         _dbConnectionString = appSettings.DbConnectionString; | ||||
|     } | ||||
|  | ||||
|     public IDbConnection GetConnection() { | ||||
|         return new SqlConnection(_dbConnectionString); | ||||
|     } | ||||
| } | ||||
|     public IDbConnection GetConnection() => | ||||
|         new SqlConnection(_dbConnectionString); | ||||
| } | ||||
							
								
								
									
										46
									
								
								MesaFabApproval.API/Services/ECNService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								MesaFabApproval.API/Services/ECNService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| namespace MesaFabApproval.API.Services; | ||||
|  | ||||
| public interface IECNService { | ||||
|     Task<bool> IsValidECNNumber(int number); | ||||
| } | ||||
|  | ||||
| public class ECNService : IECNService { | ||||
|     private readonly ILogger<ECNService> _logger; | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly IDalService _dalService; | ||||
|  | ||||
|     public ECNService(ILogger<ECNService> logger, IMemoryCache cache, IDalService dalService) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _dalService = dalService ?? throw new ArgumentNullException("IDalService not injected"); | ||||
|     } | ||||
|  | ||||
|     public async Task<bool> IsValidECNNumber(int number) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to determine if {number} is a valid ECN#"); | ||||
|  | ||||
|             if (number <= 0) return false; | ||||
|  | ||||
|             IEnumerable<int> ecnNumbers = _cache.Get<IEnumerable<int>>("ecnNumbers") ?? new HashSet<int>(); | ||||
|  | ||||
|             if (ecnNumbers.Contains(number)) return true; | ||||
|  | ||||
|             string sql = $"select count(ECNNumber) as count from ECN where ECNNumber={number}"; | ||||
|  | ||||
|             int rowsReturned = (await _dalService.QueryAsync<int>(sql)).FirstOrDefault(); | ||||
|  | ||||
|             if (rowsReturned > 0) { | ||||
|                 ecnNumbers.Append(number); | ||||
|                 _cache.Set("ecnNumbers", ecnNumbers); | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to determine if {number} is a valid ECN#, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,16 +1,21 @@ | ||||
| using System.Net; | ||||
| using System.Data; | ||||
| using System.Net; | ||||
| using System.Net.Mail; | ||||
| using System.Text; | ||||
|  | ||||
| using MesaFabApproval.API.Utilities; | ||||
| using MesaFabApproval.Models; | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Utilities; | ||||
|  | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| namespace MesaFabApproval.API.Services; | ||||
|  | ||||
| public interface IMRBService { | ||||
|     Task<IEnumerable<MRB>> GetAllMRBs(); | ||||
|     Task<MRB> GetMRBById(int id); | ||||
|     Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache); | ||||
|     Task<bool> MRBNumberIsValid(int number); | ||||
|     Task<MRB> GetMRBById(int id, bool bypassCache = false); | ||||
|     Task<MRB> GetMRBByTitle(string title, bool bypassCache); | ||||
|     Task CreateNewMRB(MRB mrb); | ||||
|     Task UpdateMRB(MRB mrb); | ||||
| @ -19,11 +24,16 @@ public interface IMRBService { | ||||
|     Task UpdateMRBAction(MRBAction mrbAction); | ||||
|     Task DeleteMRBAction(int mrbActionID, int mrbNumber); | ||||
|     Task<IEnumerable<UploadResult>> UploadAttachments(IEnumerable<IFormFile> files, int mrbNumber); | ||||
|     Task<IEnumerable<UploadResult>> UploadActionAttachments(IEnumerable<IFormFile> files, int actionId); | ||||
|     Task<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache); | ||||
|     Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache); | ||||
|     Task DeleteAttachment(MRBAttachment attachment); | ||||
|     Task NotifyNewApprovals(MRB mrb); | ||||
|     Task NotifyApprovers(MRBNotification notification); | ||||
|     Task NotifyOriginator(MRBNotification notification); | ||||
|     Task NotifyQAPreApprover(MRBNotification notification); | ||||
|     Task DeleteMRB(int mrbNumber); | ||||
|     Task ConvertActionsToCsvFile(int mrbNumber, string path); | ||||
| } | ||||
|  | ||||
| public class MRBService : IMRBService { | ||||
| @ -42,17 +52,16 @@ public class MRBService : IMRBService { | ||||
|                       IMemoryCache cache, | ||||
|                       IUserService userService, | ||||
|                       IApprovalService approvalService, | ||||
|                       ISmtpService smtpService) { | ||||
|                       ISmtpService smtpService, | ||||
|                       AppSettings appSettings) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _dalService = dalService ?? throw new ArgumentNullException("IDalService not injected"); | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _userService = userService ?? throw new ArgumentNullException("IUserService not injected"); | ||||
|         _approvalService = approvalService ?? throw new ArgumentNullException("IApprovalService not injected"); | ||||
|         _smtpService = smtpService ?? throw new ArgumentNullException("ISmtpService not injected"); | ||||
|         _siteBaseUrl = Environment.GetEnvironmentVariable("NewFabApprovalBaseUrl") ?? | ||||
|             throw new ArgumentNullException("FabApprovalBaseUrl environment variable not found"); | ||||
|         _mrbAttachmentPath = Environment.GetEnvironmentVariable("FabApprovalMrbAttachmentPath") ?? | ||||
|             throw new ArgumentNullException("FabApprovalMrbAttachmentPath environment variable not found"); | ||||
|         _siteBaseUrl = appSettings.SiteBaseUrl; | ||||
|         _mrbAttachmentPath = appSettings.MrbAttachmentPath; | ||||
|     } | ||||
|     public async Task CreateNewMRB(MRB mrb) { | ||||
|         try { | ||||
| @ -61,35 +70,37 @@ public class MRBService : IMRBService { | ||||
|             if (mrb is null) throw new ArgumentNullException("MRB cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append("insert into MRB (OriginatorID, Title, "); | ||||
|             if (mrb.SubmittedDate > DateTime.MinValue) queryBuilder.Append("SubmittedDate, "); | ||||
|             if (mrb.CloseDate < DateTime.MaxValue) queryBuilder.Append("CloseDate, "); | ||||
|             if (mrb.CancelDate < DateTime.MaxValue) queryBuilder.Append("CancelDate, "); | ||||
|             queryBuilder.Append("NumberOfLotsAffected, "); | ||||
|             if (mrb.ApprovalDate < DateTime.MaxValue) queryBuilder.Append("ApprovalDate, "); | ||||
|             queryBuilder.Append("insert into MRB (OriginatorID, Title, SubmittedDate, "); | ||||
|             queryBuilder.Append("CloseDate, CancelDate, NumberOfLotsAffected, ApprovalDate, "); | ||||
|             queryBuilder.Append("IssueDescription, CustomerImpacted, Department, Process, Val, RMANo, "); | ||||
|             queryBuilder.Append("PCRBNo, SpecsImpacted, TrainingRequired, Status, StageNo) values ("); | ||||
|             queryBuilder.Append("PCRBNo, SpecsImpacted, TrainingRequired, Status, StageNo, "); | ||||
|             queryBuilder.Append("CustomerImpactedName, ProcessECNNumber, Tool, Category) values ("); | ||||
|             queryBuilder.Append($"{mrb.OriginatorID}, '{mrb.Title}', "); | ||||
|             if (mrb.SubmittedDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"'{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CloseDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"'{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CancelDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"'{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.SubmittedDate < DateTimeUtilities.MIN_DT) | ||||
|                 mrb.SubmittedDate = DateTimeUtilities.MIN_DT; | ||||
|             queryBuilder.Append($"'{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CloseDate > DateTimeUtilities.MAX_DT) | ||||
|                 mrb.CloseDate = DateTimeUtilities.MAX_DT; | ||||
|             queryBuilder.Append($"'{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CancelDate > DateTimeUtilities.MAX_DT) | ||||
|                 mrb.CancelDate = DateTimeUtilities.MAX_DT; | ||||
|             queryBuilder.Append($"'{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"{mrb.NumberOfLotsAffected}, "); | ||||
|             if (mrb.ApprovalDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"'{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.ApprovalDate > DateTimeUtilities.MAX_DT) | ||||
|                 mrb.ApprovalDate = DateTimeUtilities.MAX_DT; | ||||
|             queryBuilder.Append($"'{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"'{mrb.IssueDescription}', {Convert.ToUInt32(mrb.CustomerImpacted)}, "); | ||||
|             queryBuilder.Append($"'{mrb.Department}', '{mrb.Process}', '{mrb.Val}', {mrb.RMANo}, {mrb.PCRBNo}, "); | ||||
|             queryBuilder.Append($"'{mrb.Department}', '{mrb.Process}', {mrb.Val}, {mrb.RMANo}, '{mrb.PCRBNo}', "); | ||||
|             queryBuilder.Append($"{Convert.ToInt32(mrb.SpecsImpacted)}, {Convert.ToInt32(mrb.TrainingRequired)}, "); | ||||
|             queryBuilder.Append($"'{mrb.Status}', {mrb.StageNo});"); | ||||
|             queryBuilder.Append($"'{mrb.Status}', {mrb.StageNo}, '{mrb.CustomerImpactedName}', "); | ||||
|             queryBuilder.Append($"{mrb.ProcessECNNumber}, '{mrb.Tool}', '{mrb.Category}');"); | ||||
|  | ||||
|             int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsCreated <= 0) throw new Exception("Unable to create new MRB"); | ||||
|  | ||||
|             mrb = await GetMRBByTitle(mrb.Title, true); | ||||
|              | ||||
|  | ||||
|             _cache.Set($"mrb{mrb.MRBNumber}", mrb, DateTimeOffset.Now.AddHours(1)); | ||||
|             _cache.Set($"mrb{mrb.Title}", mrb, DateTimeOffset.Now.AddHours(1)); | ||||
|  | ||||
| @ -105,11 +116,12 @@ public class MRBService : IMRBService { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<MRB>> GetAllMRBs() { | ||||
|     public async Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get all MRBs"); | ||||
|  | ||||
|             IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs"); | ||||
|             IEnumerable<MRB>? allMrbs = null; | ||||
|             if (!bypassCache) allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs"); | ||||
|  | ||||
|             if (allMrbs is null) { | ||||
|                 StringBuilder queryBuilder = new(); | ||||
| @ -121,9 +133,6 @@ public class MRBService : IMRBService { | ||||
|                 _cache.Set("allMrbs", allMrbs, DateTimeOffset.Now.AddHours(1)); | ||||
|             } | ||||
|  | ||||
|             if (allMrbs is null || allMrbs.Count() == 0) | ||||
|                 throw new Exception("No MRBs found"); | ||||
|  | ||||
|             return allMrbs; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to get all MRBs. Exception: {ex.Message}"); | ||||
| @ -131,13 +140,43 @@ public class MRBService : IMRBService { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<MRB> GetMRBById(int id) { | ||||
|     public async Task<bool> MRBNumberIsValid(int number) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to determine if {number} is a valid MRB#"); | ||||
|  | ||||
|             if (number <= 0) return false; | ||||
|  | ||||
|             IEnumerable<int> mrbNumbers = _cache.Get<IEnumerable<int>>("mrbNumbers") ?? | ||||
|                                             new HashSet<int>(); | ||||
|  | ||||
|             if (mrbNumbers.Contains(number)) return true; | ||||
|  | ||||
|             string sql = $"select count(MRBNumber) as count from MRB where MRBNumber={number}"; | ||||
|  | ||||
|             int rowsReturned = (await _dalService.QueryAsync<int>(sql)).FirstOrDefault(); | ||||
|  | ||||
|             if (rowsReturned > 0) { | ||||
|                 mrbNumbers.Append(number); | ||||
|                 _cache.Set("mrbNumbers", mrbNumbers); | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to determine if {number} is a valid MRB#, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<MRB> GetMRBById(int id, bool bypassCache = false) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get an MRB by ID"); | ||||
|  | ||||
|             if (id < 0) throw new ArgumentException("Invalid MRB number"); | ||||
|  | ||||
|             MRB? mrb = _cache.Get<MRB>($"mrb{id}"); | ||||
|             MRB? mrb = null; | ||||
|             if (!bypassCache) | ||||
|                 mrb = _cache.Get<MRB>($"mrb{id}"); | ||||
|  | ||||
|             if (mrb is null) { | ||||
|                 StringBuilder queryBuilder = new(); | ||||
| @ -163,10 +202,10 @@ public class MRBService : IMRBService { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get an MRB by title"); | ||||
|  | ||||
|             if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or emtpy"); | ||||
|             if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty"); | ||||
|  | ||||
|             MRB? mrb = null; | ||||
|              | ||||
|  | ||||
|             if (!bypassCache) mrb = _cache.Get<MRB>($"mrb{title}"); | ||||
|  | ||||
|             if (mrb is null) { | ||||
| @ -197,26 +236,33 @@ public class MRBService : IMRBService { | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append($"update MRB set OriginatorID = {mrb.OriginatorID}, "); | ||||
|             queryBuilder.Append($"Title = '{mrb.Title}', "); | ||||
|             if (mrb.SubmittedDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"SubmittedDate = '{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CloseDate < DateTime.MaxValue)  | ||||
|                 queryBuilder.Append($"CloseDate = '{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CancelDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"CancelDate = '{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"Title = '{mrb.Title.Replace("'", "''")}', "); | ||||
|             if (mrb.SubmittedDate < DateTimeUtilities.MIN_DT) | ||||
|                 mrb.SubmittedDate = DateTimeUtilities.MIN_DT; | ||||
|             queryBuilder.Append($"SubmittedDate = '{mrb.SubmittedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CloseDate > DateTimeUtilities.MAX_DT) | ||||
|                 mrb.CloseDate = DateTimeUtilities.MAX_DT; | ||||
|             queryBuilder.Append($"CloseDate = '{mrb.CloseDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrb.CancelDate > DateTimeUtilities.MAX_DT) | ||||
|                 mrb.CancelDate = DateTimeUtilities.MAX_DT; | ||||
|             queryBuilder.Append($"CancelDate = '{mrb.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"NumberOfLotsAffected = {mrb.NumberOfLotsAffected}, "); | ||||
|             if (mrb.ApprovalDate < DateTime.MaxValue)  | ||||
|                 queryBuilder.Append($"ApprovalDate = '{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"IssueDescription = '{mrb.IssueDescription}', "); | ||||
|             if (mrb.ApprovalDate > DateTimeUtilities.MAX_DT) | ||||
|                 mrb.ApprovalDate = DateTimeUtilities.MAX_DT; | ||||
|             queryBuilder.Append($"ApprovalDate = '{mrb.ApprovalDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"IssueDescription = '{mrb.IssueDescription.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"CustomerImpacted = {Convert.ToInt32(mrb.CustomerImpacted)}, "); | ||||
|             queryBuilder.Append($"Department = '{mrb.Department}', "); | ||||
|             queryBuilder.Append($"Process = '{mrb.Process}', "); | ||||
|             queryBuilder.Append($"Val = '{mrb.Val}', "); | ||||
|             queryBuilder.Append($"Department = '{mrb.Department.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"Process = '{mrb.Process.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"Val = {mrb.Val}, "); | ||||
|             queryBuilder.Append($"RMANo = {mrb.RMANo}, "); | ||||
|             queryBuilder.Append($"PCRBNo = {mrb.PCRBNo}, "); | ||||
|             queryBuilder.Append($"PCRBNo = '{mrb.PCRBNo}', "); | ||||
|             queryBuilder.Append($"SpecsImpacted = {Convert.ToInt32(mrb.SpecsImpacted)}, "); | ||||
|             queryBuilder.Append($"TrainingRequired = {Convert.ToInt32(mrb.TrainingRequired)}, "); | ||||
|             queryBuilder.Append($"Status = '{mrb.Status}', StageNo = {mrb.StageNo} "); | ||||
|             queryBuilder.Append($"Status = '{mrb.Status}', StageNo = {mrb.StageNo}, "); | ||||
|             queryBuilder.Append($"CustomerImpactedName = '{mrb.CustomerImpactedName.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"ProcessECNNumber = '{mrb.ProcessECNNumber}', "); | ||||
|             queryBuilder.Append($"Tool = '{mrb.Tool.Replace("'", "''")}', Category = '{mrb.Category.Replace("'", "''")}' "); | ||||
|             queryBuilder.Append($"where MRBNumber = {mrb.MRBNumber};"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
| @ -229,7 +275,7 @@ public class MRBService : IMRBService { | ||||
|             IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs"); | ||||
|             if (allMrbs is not null) { | ||||
|                 List<MRB> mrbList = allMrbs.ToList(); | ||||
|                 mrbList.RemoveAll(m => m.MRBNumber ==mrb.MRBNumber); | ||||
|                 mrbList.RemoveAll(m => m.MRBNumber == mrb.MRBNumber); | ||||
|                 mrbList.Add(mrb); | ||||
|                 _cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1)); | ||||
|             } | ||||
| @ -246,15 +292,16 @@ public class MRBService : IMRBService { | ||||
|             if (mrbAction is null) throw new ArgumentNullException("MRB action cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append("insert into MRBAction (Action, Customer, Quantity, PartNumber, LotNumber, MRBNumber) "); | ||||
|             queryBuilder.Append("insert into MRBAction (Action, Customer, Quantity, PartNumber, LotNumber, MRBNumber, "); | ||||
|             queryBuilder.Append("ConvertFrom, ConvertTo, Justification) "); | ||||
|             queryBuilder.Append($"values ('{mrbAction.Action}', '{mrbAction.Customer}', {mrbAction.Quantity}, "); | ||||
|             queryBuilder.Append($"'{mrbAction.PartNumber}', '{mrbAction.LotNumber}', {mrbAction.MRBNumber});"); | ||||
|             queryBuilder.Append($"'{mrbAction.PartNumber}', '{mrbAction.LotNumber}', {mrbAction.MRBNumber}, "); | ||||
|             queryBuilder.Append($"'{mrbAction.ConvertFrom}', '{mrbAction.ConvertTo}', '{mrbAction.Justification}');"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("Unable to create MRB action in database"); | ||||
|  | ||||
|              | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to create new MRB action. Exception: {ex.Message}"); | ||||
|             throw; | ||||
| @ -268,7 +315,7 @@ public class MRBService : IMRBService { | ||||
|             if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number"); | ||||
|  | ||||
|             IEnumerable<MRBAction>? mrbActions = null; | ||||
|              | ||||
|  | ||||
|             if (!bypassCache) | ||||
|                 _cache.Get<IEnumerable<MRBAction>>($"mrbActions{mrbNumber}"); | ||||
|  | ||||
| @ -305,15 +352,20 @@ public class MRBService : IMRBService { | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append($"update MRBAction set Action = '{mrbAction.Action}', "); | ||||
|             queryBuilder.Append($"Customer = '{mrbAction.Customer}', "); | ||||
|             queryBuilder.Append($"Customer = '{mrbAction.Customer.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"Quantity = {mrbAction.Quantity}, "); | ||||
|             queryBuilder.Append($"PartNumber = '{mrbAction.PartNumber}', "); | ||||
|             queryBuilder.Append($"LotNumber = '{mrbAction.LotNumber}', "); | ||||
|             if (mrbAction.AssignedDate > DateTime.MinValue) | ||||
|                 queryBuilder.Append($"AssignedDate= '{mrbAction.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrbAction.CompletedDate < DateTime.MaxValue) | ||||
|                 queryBuilder.Append($"CompletedDate= '{mrbAction.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"CompletedByUserID={mrbAction.CompletedByUserID} "); | ||||
|             queryBuilder.Append($"PartNumber = '{mrbAction.PartNumber.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"LotNumber = '{mrbAction.LotNumber.Replace("'", "''")}', "); | ||||
|             if (mrbAction.AssignedDate < DateTimeUtilities.MIN_DT) | ||||
|                 mrbAction.AssignedDate = DateTimeUtilities.MIN_DT; | ||||
|             queryBuilder.Append($"AssignedDate= '{mrbAction.AssignedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             if (mrbAction.CompletedDate > DateTimeUtilities.MAX_DT) | ||||
|                 mrbAction.CompletedDate = DateTimeUtilities.MAX_DT; | ||||
|             queryBuilder.Append($"CompletedDate= '{mrbAction.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"CompletedByUserID={mrbAction.CompletedByUserID}, "); | ||||
|             queryBuilder.Append($"ConvertFrom='{mrbAction.ConvertFrom.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"ConvertTo='{mrbAction.ConvertTo.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"Justification='{mrbAction.Justification.Replace("'", "''")}' "); | ||||
|             queryBuilder.Append($"where ActionID={mrbAction.ActionID};"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
| @ -366,7 +418,6 @@ public class MRBService : IMRBService { | ||||
|             if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty"); | ||||
|             if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number"); | ||||
|  | ||||
|             List<Task> taskList = new (); | ||||
|             foreach (IFormFile file in files) { | ||||
|                 try { | ||||
|                     if (file is null) throw new ArgumentException("File cannot be null"); | ||||
| @ -375,8 +426,8 @@ public class MRBService : IMRBService { | ||||
|                     string encodedName = WebUtility.HtmlEncode(file.FileName); | ||||
|                     string path = $"{_mrbAttachmentPath}\\{mrbNumber}\\{encodedName}"; | ||||
|  | ||||
|                     taskList.Add(SaveFileToFileSystem(file, path)); | ||||
|                     taskList.Add(SaveAttachmentInDb(file, path, mrbNumber)); | ||||
|                     await FileUtilities.SaveFileToFileSystem(file, path); | ||||
|                     await SaveAttachmentInDb(file, path, mrbNumber); | ||||
|  | ||||
|                     UploadResult uploadResult = new() { | ||||
|                         UploadSuccessful = true, | ||||
| @ -393,11 +444,55 @@ public class MRBService : IMRBService { | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return uploadResults; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to upload attachment. Exception: {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<UploadResult>> UploadActionAttachments(IEnumerable<IFormFile> files, int actionId) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to upload action attachments"); | ||||
|  | ||||
|             List<UploadResult> uploadResults = new(); | ||||
|  | ||||
|             if (files is null) throw new ArgumentNullException("Files cannot be null"); | ||||
|             if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty"); | ||||
|             if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID"); | ||||
|  | ||||
|             List<Task> taskList = new(); | ||||
|             foreach (IFormFile file in files) { | ||||
|                 try { | ||||
|                     if (file is null) throw new ArgumentException("File cannot be null"); | ||||
|                     if (file.Length <= 0) throw new ArgumentException("File size cannot be zero"); | ||||
|  | ||||
|                     string encodedName = WebUtility.HtmlEncode(file.FileName); | ||||
|                     string path = $"{_mrbAttachmentPath}\\{actionId}\\{encodedName}"; | ||||
|  | ||||
|                     taskList.Add(FileUtilities.SaveFileToFileSystem(file, path)); | ||||
|                     taskList.Add(SaveActionAttachmentInDb(file, path, actionId)); | ||||
|  | ||||
|                     UploadResult uploadResult = new() { | ||||
|                         UploadSuccessful = true, | ||||
|                         FileName = file.Name | ||||
|                     }; | ||||
|                     uploadResults.Add(uploadResult); | ||||
|                 } catch (Exception ex) { | ||||
|                     UploadResult uploadResult = new() { | ||||
|                         UploadSuccessful = false, | ||||
|                         FileName = file.Name, | ||||
|                         Error = ex.Message | ||||
|                     }; | ||||
|                     uploadResults.Add(uploadResult); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Task.WaitAll(taskList.ToArray()); | ||||
|  | ||||
|             return uploadResults; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to upload attachment. Exception: {ex.Message}"); | ||||
|             _logger.LogError($"An exception occurred when attempting to upload action attachment. Exception: {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| @ -430,6 +525,41 @@ public class MRBService : IMRBService { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to get all action attachments for MRB {mrbNumber}"); | ||||
|  | ||||
|             if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#"); | ||||
|  | ||||
|             List<MRBActionAttachment>? attachments = null; | ||||
|             if (!bypassCache) | ||||
|                 attachments = _cache.Get<List<MRBActionAttachment>>($"mrbActionAttachments{mrbNumber}"); | ||||
|  | ||||
|             if (attachments is null) { | ||||
|                 attachments = new List<MRBActionAttachment>(); | ||||
|  | ||||
|                 foreach (MRBAction action in await GetMRBActionsForMRB(mrbNumber, false)) { | ||||
|                     string sql = $"select * from MRBActionAttachment where ActionID = {action.ActionID};"; | ||||
|  | ||||
|                     IEnumerable<MRBActionAttachment> newAttachments = | ||||
|                         (await _dalService.QueryAsync<MRBActionAttachment>(sql)).ToList(); | ||||
|  | ||||
|                     attachments.AddRange(newAttachments); | ||||
|                 } | ||||
|  | ||||
|                 _cache.Set($"mrbActionAttachments{mrbNumber}", attachments, DateTimeOffset.Now.AddMinutes(15)); | ||||
|             } | ||||
|  | ||||
|             return attachments; | ||||
|         } catch (Exception ex) { | ||||
|             StringBuilder errMsgBuilder = new(); | ||||
|             errMsgBuilder.Append($"An error occurred when attempting to get all attachments for MRB action {mrbNumber}. "); | ||||
|             errMsgBuilder.Append($"Exception: {ex.Message}"); | ||||
|             _logger.LogError(errMsgBuilder.ToString()); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteAttachment(MRBAttachment attachment) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to delete an attachment"); | ||||
| @ -443,7 +573,7 @@ public class MRBService : IMRBService { | ||||
|  | ||||
|             int rowsDeleted = await _dalService.ExecuteAsync(sql); | ||||
|  | ||||
|             if (rowsDeleted <= 0)  | ||||
|             if (rowsDeleted <= 0) | ||||
|                 throw new Exception($"No attachments found in the database with attachment ID {attachment.AttachmentID}"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to delete an attachment. Exception: {ex.Message}"); | ||||
| @ -459,7 +589,7 @@ public class MRBService : IMRBService { | ||||
|  | ||||
|             IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(mrb.MRBNumber, true); | ||||
|  | ||||
|             List<Approval> approvalsNeedingNotification = approvals.Where(a => a.NotifyDate <= DateTime.MinValue).ToList(); | ||||
|             List<Approval> approvalsNeedingNotification = approvals.Where(a => a.NotifyDate <= DateTimeUtilities.MIN_DT).ToList(); | ||||
|  | ||||
|             HashSet<string> emailsAlreadySent = new(); | ||||
|             foreach (Approval approval in approvalsNeedingNotification) { | ||||
| @ -473,11 +603,12 @@ public class MRBService : IMRBService { | ||||
|  | ||||
|                     List<MailAddress> ccAddresses = new(); | ||||
|  | ||||
|                     string subject = $"[New Task] Mesa Fab Approval - MRB# {mrb.MRBNumber}"; | ||||
|                     string subject = $"[New Task] Mesa Fab Approval - MRB# {mrb.MRBNumber} - {mrb.Title}"; | ||||
|  | ||||
|                     StringBuilder bodyBuilder = new(); | ||||
|                     bodyBuilder.Append($"You have been assigned a new task in Mesa Fab Approval for issue {approval.IssueID}. The assigned role is {approval.SubRoleCategoryItem}. <br /> <br />"); | ||||
|                     bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?jwt=jwt&refreshToken=refreshToken&redirectPath=/mrb/{approval.IssueID} to view the task."); | ||||
|                     bodyBuilder.Append($"MRB# {mrb.MRBNumber} [{mrb.Title}] is ready for your approval. "); | ||||
|                     bodyBuilder.Append($"The assigned role is {approval.SubRoleCategoryItem}. <br /> <br />"); | ||||
|                     bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{approval.IssueID} to view the task."); | ||||
|  | ||||
|                     await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); | ||||
|  | ||||
| @ -501,10 +632,8 @@ public class MRBService : IMRBService { | ||||
|  | ||||
|             IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(notification.MRB.MRBNumber, true); | ||||
|  | ||||
|             List<Approval> approvalsNeedingNotification = approvals.Where(a => a.NotifyDate <= DateTime.MinValue).ToList(); | ||||
|  | ||||
|             HashSet<string> emailsAlreadySent = new(); | ||||
|             foreach (Approval approval in approvalsNeedingNotification) { | ||||
|             foreach (Approval approval in approvals) { | ||||
|                 User user = await _userService.GetUserByUserId(approval.UserID); | ||||
|  | ||||
|                 if (!emailsAlreadySent.Contains(user.Email)) { | ||||
| @ -515,11 +644,11 @@ public class MRBService : IMRBService { | ||||
|  | ||||
|                     List<MailAddress> ccAddresses = new(); | ||||
|  | ||||
|                     string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber}"; | ||||
|                     string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}"; | ||||
|  | ||||
|                     StringBuilder bodyBuilder = new(); | ||||
|                     bodyBuilder.Append($"{notification.Message} <br /> <br />"); | ||||
|                     bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?jwt=jwt&refreshToken=refreshToken&redirectPath=/mrb/{approval.IssueID} to view the MRB."); | ||||
|                     bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{approval.IssueID} to view the MRB."); | ||||
|  | ||||
|                     await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); | ||||
|  | ||||
| @ -548,11 +677,11 @@ public class MRBService : IMRBService { | ||||
|  | ||||
|             List<MailAddress> ccAddresses = new(); | ||||
|  | ||||
|             string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber}"; | ||||
|             string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}"; | ||||
|  | ||||
|             StringBuilder bodyBuilder = new(); | ||||
|             bodyBuilder.Append($"{notification.Message} <br /> <br />"); | ||||
|             bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?jwt=jwt&refreshToken=refreshToken&redirectPath=/mrb/{notification.MRB.MRBNumber} to view the MRB."); | ||||
|             bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{notification.MRB.MRBNumber} to view the MRB."); | ||||
|  | ||||
|             await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); | ||||
|         } catch (Exception ex) { | ||||
| @ -561,24 +690,202 @@ public class MRBService : IMRBService { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task SaveFileToFileSystem(IFormFile file, string path) { | ||||
|     public async Task NotifyQAPreApprover(MRBNotification notification) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to save file to file system"); | ||||
|             _logger.LogInformation("Attempting to send notification to QA pre approver"); | ||||
|  | ||||
|             if (file is null) throw new ArgumentNullException("File cannot be null"); | ||||
|             if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||
|             if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null"); | ||||
|             if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); | ||||
|  | ||||
|             string roleName = "QA_PRE_APPROVAL"; | ||||
|  | ||||
|             int roleId = await _approvalService.GetRoleIdForRoleName(roleName); | ||||
|  | ||||
|             if (roleId <= 0) throw new Exception($"could not find {roleName} role ID"); | ||||
|  | ||||
|             SubRole? subRole = (await _approvalService.GetSubRolesForSubRoleName(roleName, roleId)).FirstOrDefault(); | ||||
|  | ||||
|             if (subRole is null) | ||||
|                 throw new Exception("Unable to find QA pre approver role"); | ||||
|  | ||||
|             IEnumerable<User> members = await _approvalService.GetApprovalGroupMembers(subRole.SubRoleID); | ||||
|  | ||||
|             List<MailAddress> toAddresses = new(); | ||||
|             foreach (User member in members) | ||||
|                 toAddresses.Add(new MailAddress(member.Email)); | ||||
|  | ||||
|             List<MailAddress> ccAddresses = new(); | ||||
|  | ||||
|             string subject = $"[Update] Mesa Fab Approval - MRB# {notification.MRB.MRBNumber} - {notification.MRB.Title}"; | ||||
|  | ||||
|             StringBuilder bodyBuilder = new(); | ||||
|             bodyBuilder.Append($"{notification.Message} <br /> <br />"); | ||||
|             bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=mrb/{notification.MRB.MRBNumber} to view the MRB."); | ||||
|  | ||||
|             await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to send notification to QA pre approver, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteMRB(int mrbNumber) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to delete MRB# {mrbNumber}"); | ||||
|  | ||||
|             if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB number"); | ||||
|  | ||||
|             string sql = $"delete from MRB where MRBNumber={mrbNumber}"; | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(sql); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception($"MRB {mrbNumber} not deleted from database"); | ||||
|  | ||||
|             IEnumerable<MRB>? allMrbs = _cache.Get<IEnumerable<MRB>>("allMrbs"); | ||||
|             if (allMrbs is not null) { | ||||
|                 List<MRB> mrbList = allMrbs.ToList(); | ||||
|                 mrbList.RemoveAll(m => m.MRBNumber == mrbNumber); | ||||
|                 _cache.Set("allMrbs", mrbList, DateTimeOffset.Now.AddHours(1)); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to delete MRB {mrbNumber}, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task ConvertActionsToCsvFile(int mrbNumber, string path) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to convert MRB {mrbNumber} actions to a CSV file"); | ||||
|  | ||||
|             if (!(await MRBNumberIsValid(mrbNumber))) throw new ArgumentException($"{mrbNumber} is not a valid "); | ||||
|             if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty"); | ||||
|  | ||||
|             if (File.Exists(path)) throw new Exception($"A file already exists with name {file.FileName}"); | ||||
|             if (File.Exists(path)) File.Delete(path); | ||||
|  | ||||
|             string? directoryPath = Path.GetDirectoryName(path); | ||||
|             if (!string.IsNullOrWhiteSpace(directoryPath)) | ||||
|                 Directory.CreateDirectory(directoryPath); | ||||
|  | ||||
|             using (FileStream stream = File.Create(path)) { | ||||
|                 await file.OpenReadStream().CopyToAsync(stream); | ||||
|             IEnumerable<MRBAction> actions = await GetMRBActionsForMRB(mrbNumber, false); | ||||
|  | ||||
|             DataTable dt = await ConvertActionsToDataTable(actions); | ||||
|  | ||||
|             using StreamWriter sw = new StreamWriter(path, false); | ||||
|  | ||||
|             for (int i = 0; i < dt.Columns.Count; i++) { | ||||
|                 sw.Write(dt.Columns[i]); | ||||
|                 if (i < dt.Columns.Count - 1) sw.Write(","); | ||||
|             } | ||||
|  | ||||
|             sw.Write(sw.NewLine); | ||||
|  | ||||
|             foreach (DataRow dr in dt.Rows) { | ||||
|                 for (int i = 0; i < dt.Columns.Count; i++) { | ||||
|                     if (!Convert.IsDBNull(dr[i])) { | ||||
|                         string? value = dr[i].ToString(); | ||||
|                         if (value is null) { | ||||
|                             sw.Write(""); | ||||
|                         } else if (value.Contains(',')) { | ||||
|                             value = String.Format("\"{0}\"", value); | ||||
|                             sw.Write(value); | ||||
|                         } else { | ||||
|                             sw.Write(dr[i].ToString()); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if (i < dt.Columns.Count - 1) { | ||||
|                         sw.Write(","); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 sw.Write(sw.NewLine); | ||||
|             } | ||||
|  | ||||
|             sw.Close(); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to save file to file system. Exception: {ex.Message}"); | ||||
|             _logger.LogError($"Unable to convert MRB {mrbNumber} actions to a CSV file, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task<DataTable> ConvertActionsToDataTable(IEnumerable<MRBAction> actions) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to convert MRB actions to a DataTable"); | ||||
|  | ||||
|             if (actions is null) throw new ArgumentNullException("MRB actions cannot be null"); | ||||
|  | ||||
|             DataTable dt = new(); | ||||
|  | ||||
|             if (actions.Count() > 0 && actions.First().Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) { | ||||
|                 dt.Columns.Add("Action", typeof(string)); | ||||
|                 dt.Columns.Add("From Customer", typeof(string)); | ||||
|                 dt.Columns.Add("From Part Number", typeof(string)); | ||||
|                 dt.Columns.Add("Batch Number / Lot Number", typeof(string)); | ||||
|                 dt.Columns.Add("Qty", typeof(string)); | ||||
|                 dt.Columns.Add("To Customer", typeof(string)); | ||||
|                 dt.Columns.Add("To Part Number", typeof(string)); | ||||
|                 dt.Columns.Add("Assigned Date", typeof(string)); | ||||
|                 dt.Columns.Add("Completed Date", typeof(string)); | ||||
|                 dt.Columns.Add("Completed By", typeof(string)); | ||||
|  | ||||
|                 foreach (MRBAction action in actions) { | ||||
|                     if (action.CompletedByUser is null && action.CompletedByUserID > 0) | ||||
|                         action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID); | ||||
|  | ||||
|                     string convertFromCustomer = string.Empty; | ||||
|                     string convertFromPart = string.Empty; | ||||
|                     string convertToCustomer = string.Empty; | ||||
|                     string convertToPart = string.Empty; | ||||
|  | ||||
|                     string[] convertFrom = action.ConvertFrom.Split(" "); | ||||
|                     if (convertFrom.Length > 1) { | ||||
|                         convertFromCustomer = convertFrom[0]; | ||||
|                         foreach (string partStr in convertFrom.Skip(1)) | ||||
|                             convertFromPart += partStr; | ||||
|                     } | ||||
|  | ||||
|                     string[] convertTo = action.ConvertTo.Split(" "); | ||||
|                     if (convertTo.Length > 1) { | ||||
|                         convertToCustomer = convertTo[0]; | ||||
|                         foreach (string partStr in convertTo.Skip(1)) | ||||
|                             convertToPart += partStr; | ||||
|                     } | ||||
|  | ||||
|                     dt.Rows.Add(action.Action, convertFromCustomer, convertFromPart, action.Quantity.ToString(), | ||||
|                                 convertToCustomer, convertToPart, | ||||
|                                 DateTimeUtilities.GetDateAsStringMinDefault(action.AssignedDate), | ||||
|                                 DateTimeUtilities.GetDateAsStringMaxDefault(action.CompletedDate), | ||||
|                                 action.CompletedByUser is null ? "" : action.CompletedByUser.GetFullName()); | ||||
|                 } | ||||
|             } else { | ||||
|                 dt.Columns.Add("Action", typeof(string)); | ||||
|                 dt.Columns.Add("Customer", typeof(string)); | ||||
|                 dt.Columns.Add("Qty", typeof(string)); | ||||
|                 dt.Columns.Add("Convert From", typeof(string)); | ||||
|                 dt.Columns.Add("Convert To", typeof(string)); | ||||
|                 dt.Columns.Add("Part Number", typeof(string)); | ||||
|                 dt.Columns.Add("Batch Number / Lot Number", typeof(string)); | ||||
|                 dt.Columns.Add("Justification", typeof(string)); | ||||
|                 dt.Columns.Add("Assigned Date", typeof(string)); | ||||
|                 dt.Columns.Add("Completed Date", typeof(string)); | ||||
|                 dt.Columns.Add("Completed By", typeof(string)); | ||||
|  | ||||
|                 foreach (MRBAction action in actions) { | ||||
|                     if (action.CompletedByUser is null && action.CompletedByUserID > 0) | ||||
|                         action.CompletedByUser = await _userService.GetUserByUserId(action.CompletedByUserID); | ||||
|  | ||||
|                     dt.Rows.Add(action.Action, action.Customer, action.Quantity.ToString(), action.ConvertFrom, action.ConvertTo, | ||||
|                                 action.PartNumber, action.LotNumber, action.Justification, | ||||
|                                 DateTimeUtilities.GetDateAsStringMinDefault(action.AssignedDate), | ||||
|                                 DateTimeUtilities.GetDateAsStringMaxDefault(action.CompletedDate), | ||||
|                                 action.CompletedByUser is null ? "" : action.CompletedByUser.GetFullName()); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return dt; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to convert MRB actions to a DataTable, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| @ -601,8 +908,31 @@ public class MRBService : IMRBService { | ||||
|             if (rowsAffected <= 0) | ||||
|                 throw new Exception("Unable to insert attachment in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to save file to file system. Exception: {ex.Message}"); | ||||
|             _logger.LogError($"An exception occurred when attempting to save file to DB. Exception: {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     private async Task SaveActionAttachmentInDb(IFormFile file, string path, int actionId) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to save action attachment to database"); | ||||
|  | ||||
|             if (file is null) throw new ArgumentNullException("File cannot be null"); | ||||
|             if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty"); | ||||
|             if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append("insert into MRBActionAttachment (ActionID, FileName, UploadDate, Path) "); | ||||
|             queryBuilder.Append($"values ({actionId}, '{file.FileName}', "); | ||||
|             queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{path}');"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsAffected <= 0) | ||||
|                 throw new Exception("Unable to insert action attachment in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"An exception occurred when attempting to save file to DB. Exception: {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										767
									
								
								MesaFabApproval.API/Services/PCRBService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										767
									
								
								MesaFabApproval.API/Services/PCRBService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,767 @@ | ||||
| using System.Net; | ||||
| using System.Net.Mail; | ||||
| using System.Text; | ||||
|  | ||||
| using MesaFabApproval.API.Utilities; | ||||
| using MesaFabApproval.Models; | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Utilities; | ||||
|  | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| namespace MesaFabApproval.API.Services; | ||||
|  | ||||
| public interface IPCRBService { | ||||
|     Task CreateNewPCRB(PCRB pcrb); | ||||
|     Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache); | ||||
|     Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task<PCRB> GetPCRBByTitle(string title, bool bypassCache); | ||||
|     Task UpdatePCRB(PCRB pcrb); | ||||
|     Task DeletePCRB(int planNumber); | ||||
|     Task<UploadResult> UploadAttachment(IFormFile file, PCRBAttachment attachment); | ||||
|     Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task UpdateAttachment(PCRBAttachment attachment); | ||||
|     Task DeleteAttachment(PCRBAttachment attachment); | ||||
|     Task CreateNewActionItem(PCRBActionItem actionItem); | ||||
|     Task UpdateActionItem(PCRBActionItem actionItem); | ||||
|     Task DeleteActionItem(int id); | ||||
|     Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task CreateNewAttendee(PCRBAttendee attendee); | ||||
|     Task UpdateAttendee(PCRBAttendee attendee); | ||||
|     Task DeleteAttendee(int id); | ||||
|     Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task CreatePCR3Document(PCR3Document document); | ||||
|     Task UpdatePCR3Document(PCR3Document document); | ||||
|     Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task NotifyNewApprovals(PCRB pcrb); | ||||
|     Task NotifyApprovers(PCRBNotification notification); | ||||
|     Task NotifyOriginator(PCRBNotification notification); | ||||
|     Task NotifyResponsiblePerson(PCRBActionItemNotification notification); | ||||
| } | ||||
|  | ||||
| public class PCRBService : IPCRBService { | ||||
|     private readonly ILogger<PCRBService> _logger; | ||||
|     private readonly IDalService _dalService; | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly IUserService _userService; | ||||
|     private readonly IApprovalService _approvalService; | ||||
|     private readonly ISmtpService _smtpService; | ||||
|  | ||||
|     private readonly string _pcrbAttachmentPath; | ||||
|     private readonly string _siteBaseUrl; | ||||
|  | ||||
|     public PCRBService(ILogger<PCRBService> logger, | ||||
|                        IDalService dalService, | ||||
|                        IMemoryCache cache, | ||||
|                        IUserService userService, | ||||
|                        IApprovalService approvalService, | ||||
|                        ISmtpService smtpService, | ||||
|                        AppSettings appSettings) { | ||||
|         _logger = logger ?? throw new ArgumentNullException("ILogger not injected"); | ||||
|         _dalService = dalService ?? throw new ArgumentNullException("IDalService not injected"); | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _userService = userService ?? throw new ArgumentNullException("IUserService not injected"); | ||||
|         _approvalService = approvalService ?? | ||||
|             throw new ArgumentNullException("IApprovalService not injected"); | ||||
|         _smtpService = smtpService ?? throw new ArgumentNullException("ISmtpService not injected"); | ||||
|         _siteBaseUrl = appSettings.SiteBaseUrl; | ||||
|         _pcrbAttachmentPath = appSettings.PcrbAttachmentPath; | ||||
|     } | ||||
|  | ||||
|     public async Task CreateNewPCRB(PCRB pcrb) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to create new PCRB"); | ||||
|  | ||||
|             if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append("insert into CCChangeControl (OwnerID, Title, ChangeLevel, ReasonForChange, "); | ||||
|             queryBuilder.Append("ChangeDescription, IsITAR, CurrentStep, InsertTimeStamp, LastUpdateDate) "); | ||||
|             queryBuilder.Append($"values ({pcrb.OwnerID}, '{pcrb.Title}', '{pcrb.ChangeLevel}', "); | ||||
|             queryBuilder.Append($"'{pcrb.ReasonForChange}', '{pcrb.ChangeDescription}', "); | ||||
|             queryBuilder.Append($"{Convert.ToInt32(pcrb.IsITAR)}, {pcrb.CurrentStep}, "); | ||||
|             queryBuilder.Append($"'{pcrb.InsertTimeStamp.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}')"); | ||||
|  | ||||
|             int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsCreated <= 0) throw new Exception("unable to insert new PCRB in the database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to create new PCRB, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get all PCRBs"); | ||||
|  | ||||
|             IEnumerable<PCRB>? allPCRBs = null; | ||||
|             if (!bypassCache) allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs"); | ||||
|  | ||||
|             if (allPCRBs is null) { | ||||
|                 string sql = "select * from CCChangeControl"; | ||||
|  | ||||
|                 allPCRBs = (await _dalService.QueryAsync<PCRB>(sql)).ToList(); | ||||
|  | ||||
|                 foreach (PCRB pcrb in allPCRBs) { | ||||
|                     if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) | ||||
|                         pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); | ||||
|                 } | ||||
|  | ||||
|                 _cache.Set("allPCRBs", allPCRBs, DateTimeOffset.Now.AddHours(1)); | ||||
|             } | ||||
|  | ||||
|             if (allPCRBs is null || allPCRBs.Count() == 0) | ||||
|                 throw new Exception("no PCRBs found"); | ||||
|  | ||||
|             return allPCRBs; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to get all PCRBs, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get a PCRB by plan#"); | ||||
|  | ||||
|             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB#"); | ||||
|  | ||||
|             PCRB? pcrb = null; | ||||
|  | ||||
|             if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{planNumber}"); | ||||
|  | ||||
|             if (pcrb is null) { | ||||
|                 string sql = $"select * from CCChangeControl where PlanNumber={planNumber}"; | ||||
|  | ||||
|                 pcrb = (await _dalService.QueryAsync<PCRB>(sql)).FirstOrDefault(); | ||||
|  | ||||
|                 if (pcrb is not null) { | ||||
|                     if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) | ||||
|                         pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); | ||||
|  | ||||
|                     _cache.Set($"pcrb{planNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|                     _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (pcrb is null) throw new Exception($"unable to find PCRB {planNumber}"); | ||||
|  | ||||
|             return pcrb; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to get PCRB by Plan #, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<PCRB> GetPCRBByTitle(string title, bool bypassCache) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to get a PCRB by title"); | ||||
|  | ||||
|             if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty"); | ||||
|  | ||||
|             PCRB? pcrb = null; | ||||
|  | ||||
|             if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{title}"); | ||||
|  | ||||
|             if (pcrb is null) { | ||||
|                 string sql = $"select * from CCChangeControl where Title='{title}'"; | ||||
|  | ||||
|                 pcrb = (await _dalService.QueryAsync<PCRB>(sql)).FirstOrDefault(); | ||||
|  | ||||
|                 if (pcrb is not null) { | ||||
|                     if (string.IsNullOrWhiteSpace(pcrb.OwnerName) && pcrb.OwnerID > 0) | ||||
|                         pcrb.OwnerName = (await _userService.GetUserByUserId(pcrb.OwnerID)).GetFullName(); | ||||
|  | ||||
|                     _cache.Set($"pcrb{title}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|                     _cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (pcrb is null) throw new Exception($"unable to find PCRB {title}"); | ||||
|  | ||||
|             return pcrb; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to get PCRB by title, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task UpdatePCRB(PCRB pcrb) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to update PCRB"); | ||||
|  | ||||
|             if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append($"update CCChangeControl set OwnerID={pcrb.OwnerID}, "); | ||||
|             queryBuilder.Append($"Title='{pcrb.Title.Replace("'", "''")}', ChangeLevel='{pcrb.ChangeLevel}', "); | ||||
|             queryBuilder.Append($"CurrentStep={pcrb.CurrentStep}, ReasonForChange='{pcrb.ReasonForChange.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"ChangeDescription='{pcrb.ChangeDescription.Replace("'", "''")}', "); | ||||
|             queryBuilder.Append($"IsITAR={Convert.ToInt32(pcrb.IsITAR)}, "); | ||||
|             queryBuilder.Append($"InsertTimeStamp='{pcrb.InsertTimeStamp.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"ClosedDate='{pcrb.ClosedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"LastUpdateDate='{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}' "); | ||||
|             queryBuilder.Append($"where PlanNumber={pcrb.PlanNumber}"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("unable to perform update in database"); | ||||
|  | ||||
|             _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|             _cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|  | ||||
|             IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs"); | ||||
|             if (allPCRBs is not null) { | ||||
|                 List<PCRB> pcrbList = allPCRBs.ToList(); | ||||
|                 pcrbList.RemoveAll(p => p.PlanNumber == pcrb.PlanNumber); | ||||
|                 pcrbList.Add(pcrb); | ||||
|                 _cache.Set("allPCRBs", pcrbList, DateTimeOffset.Now.AddHours(1)); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to update PCRB, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task DeletePCRB(int planNumber) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to delete PCRB {planNumber}"); | ||||
|  | ||||
|             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan #"); | ||||
|  | ||||
|             string sql = $"delete from CCChangeControl where PlanNumber={planNumber}"; | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(sql); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("delete operation failed in database"); | ||||
|  | ||||
|             IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs"); | ||||
|             if (allPCRBs is not null) { | ||||
|                 List<PCRB> pcrbList = allPCRBs.ToList(); | ||||
|                 pcrbList.RemoveAll(p => p.PlanNumber == planNumber); | ||||
|                 _cache.Set("allPCRBs", pcrbList, DateTimeOffset.Now.AddHours(1)); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to delete PCRB {planNumber}, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<UploadResult> UploadAttachment(IFormFile file, PCRBAttachment attachment) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to upload attachment"); | ||||
|  | ||||
|             UploadResult? uploadResult = null; | ||||
|  | ||||
|             if (file is null) throw new ArgumentNullException("File cannot be null"); | ||||
|             if (file.Length <= 0) throw new ArgumentException("File size cannot be zero"); | ||||
|             if (attachment is null) throw new ArgumentNullException("Attachment cannot be null"); | ||||
|  | ||||
|             try { | ||||
|                 string encodedName = WebUtility.HtmlEncode(file.FileName); | ||||
|                 string path = $"{_pcrbAttachmentPath}\\{attachment.PlanNumber}\\{attachment.Step}\\{encodedName}"; | ||||
|  | ||||
|                 attachment.Path = path; | ||||
|  | ||||
|                 await FileUtilities.SaveFileToFileSystem(file, path); | ||||
|                 await SaveAttachmentInDb(file, attachment); | ||||
|  | ||||
|                 uploadResult = new() { | ||||
|                     UploadSuccessful = true, | ||||
|                     FileName = file.FileName | ||||
|                 }; | ||||
|             } catch (Exception ex) { | ||||
|                 uploadResult = new() { | ||||
|                     UploadSuccessful = false, | ||||
|                     FileName = file.FileName, | ||||
|                     Error = ex.Message | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             return uploadResult; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to upload attachment, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to get all attachments for PCRB Plan# {planNumber}"); | ||||
|  | ||||
|             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); | ||||
|  | ||||
|             IEnumerable<PCRBAttachment>? attachments = null; | ||||
|  | ||||
|             if (!bypassCache) | ||||
|                 attachments = _cache.Get<IEnumerable<PCRBAttachment>>($"pcrbAttachments{planNumber}"); | ||||
|  | ||||
|             if (attachments is null) { | ||||
|                 string sql = $"select * from CCAttachment where PlanNumber={planNumber}"; | ||||
|  | ||||
|                 attachments = await _dalService.QueryAsync<PCRBAttachment>(sql); | ||||
|  | ||||
|                 _cache.Set($"pcrbAttachments{planNumber}", attachments, DateTimeOffset.Now.AddMinutes(15)); | ||||
|             } | ||||
|  | ||||
|             return attachments; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to get all attachments for PCRB Plan# {planNumber}, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task UpdateAttachment(PCRBAttachment attachment) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to update an attachment"); | ||||
|  | ||||
|             if (attachment is null) | ||||
|                 throw new ArgumentNullException("attachment cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append($"update CCAttachment "); | ||||
|             queryBuilder.Append($"set Title='{attachment.Title.Replace("'", "''")}' where ID={attachment.ID}"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("update failed in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to update attachment, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteAttachment(PCRBAttachment attachment) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to update an attachment"); | ||||
|  | ||||
|             if (attachment is null) | ||||
|                 throw new ArgumentNullException("attachment cannot be null"); | ||||
|  | ||||
|             if (!File.Exists(attachment.Path)) throw new FileNotFoundException("No file found at provided path"); | ||||
|  | ||||
|             File.Delete(attachment.Path); | ||||
|  | ||||
|             string sql = $"delete from CCAttachment where ID={attachment.ID}"; | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(sql); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("update failed in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to update attachment, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task CreateNewActionItem(PCRBActionItem actionItem) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to create new action item"); | ||||
|  | ||||
|             if (actionItem is null) throw new ArgumentNullException("action item cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append("insert into CCPCRBActionItem (Name, Gating, ClosedStatus, ClosedDate, "); | ||||
|             queryBuilder.Append("ClosedByID, UploadedByID, UploadedDateTime, ResponsiblePersonID, PlanNumber, "); | ||||
|             queryBuilder.Append($"Step) values ('{actionItem.Name}', {Convert.ToInt32(actionItem.Gating)}, "); | ||||
|             queryBuilder.Append($"{Convert.ToInt32(actionItem.ClosedStatus)}, "); | ||||
|             DateTime closedDateCopy = actionItem.ClosedDate ?? DateTimeUtilities.MAX_DT; | ||||
|             queryBuilder.Append($"'{closedDateCopy.ToString("yyyy-MM-dd HH:mm:ss")}', {actionItem.ClosedByID}, "); | ||||
|             queryBuilder.Append($"{actionItem.UploadedByID}, '{actionItem.UploadedDateTime.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"{actionItem.ResponsiblePersonID}, {actionItem.PlanNumber}, "); | ||||
|             queryBuilder.Append($"{actionItem.Step});"); | ||||
|  | ||||
|             int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsCreated <= 0) throw new Exception("unable to insert new action item in the database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to create new action item, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task UpdateActionItem(PCRBActionItem actionItem) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to update an action item"); | ||||
|  | ||||
|             if (actionItem is null) | ||||
|                 throw new ArgumentNullException("action item cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append($"update CCPCRBActionItem set Name='{actionItem.Name.Replace("'", "''")}', Gating={Convert.ToInt32(actionItem.Gating)}, "); | ||||
|             queryBuilder.Append($"ClosedStatus={Convert.ToInt32(actionItem.ClosedStatus)}, "); | ||||
|             DateTime closedDateCopy = actionItem.ClosedDate ?? DateTimeUtilities.MAX_DT; | ||||
|             queryBuilder.Append($"ClosedDate='{closedDateCopy.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"ClosedByID={actionItem.ClosedByID}, ResponsiblePersonID={actionItem.ResponsiblePersonID}, "); | ||||
|             queryBuilder.Append($"Step={actionItem.Step} where ID={actionItem.ID}"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("update failed in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to update attachment, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteActionItem(int id) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to delete action item {id}"); | ||||
|  | ||||
|             if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB action item ID"); | ||||
|  | ||||
|             string sql = $"delete from CCPCRBActionItem where ID={id}"; | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(sql); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("delete operation failed in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to delete action item {id}, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to get all action items for PCRB plan# {planNumber}"); | ||||
|  | ||||
|             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan#"); | ||||
|  | ||||
|             IEnumerable<PCRBActionItem>? actionItems = null; | ||||
|  | ||||
|             if (!bypassCache) | ||||
|                 actionItems = _cache.Get<IEnumerable<PCRBActionItem>>($"pcrbActionItems{planNumber}"); | ||||
|  | ||||
|             if (actionItems is null) { | ||||
|                 string sql = $"select * from CCPCRBActionItem where PlanNumber={planNumber}"; | ||||
|  | ||||
|                 actionItems = await _dalService.QueryAsync<PCRBActionItem>(sql); | ||||
|  | ||||
|                 _cache.Set($"pcrbActionItems{planNumber}", actionItems, DateTimeOffset.Now.AddMinutes(15)); | ||||
|             } | ||||
|  | ||||
|             return actionItems; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to get all action items for PCRB plan# {planNumber}, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task CreateNewAttendee(PCRBAttendee attendee) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to create new attendee"); | ||||
|  | ||||
|             if (attendee is null) throw new ArgumentNullException("attendee item cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append("insert into CCPCRBAttendee (PlanNumber, JobTitle, Location, Attended, AttendeeID, Step) "); | ||||
|             queryBuilder.Append($"values ({attendee.PlanNumber}, '{attendee.JobTitle}', '{attendee.Location}', "); | ||||
|             queryBuilder.Append($"{Convert.ToInt32(attendee.Attended)}, {attendee.AttendeeID}, "); | ||||
|             queryBuilder.Append($"{attendee.Step});"); | ||||
|  | ||||
|             int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsCreated <= 0) throw new Exception("unable to insert new attendee in the database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to create new attendee, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task UpdateAttendee(PCRBAttendee attendee) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to update an attendee"); | ||||
|  | ||||
|             if (attendee is null) | ||||
|                 throw new ArgumentNullException("attendee cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append($"update CCPCRBAttendee set JobTitle='{attendee.JobTitle}', "); | ||||
|             queryBuilder.Append($"Location='{attendee.Location}', Attended={Convert.ToInt32(attendee.Attended)}, "); | ||||
|             queryBuilder.Append($"AttendeeID={attendee.AttendeeID}, "); | ||||
|             queryBuilder.Append($"Step={attendee.Step} where ID={attendee.ID}"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("update failed in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to update attendee, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteAttendee(int id) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to delete attendee {id}"); | ||||
|  | ||||
|             if (id <= 0) throw new ArgumentException($"{id} is not a valid attendee ID"); | ||||
|  | ||||
|             string sql = $"delete from CCPCRBAttendee where ID={id}"; | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(sql); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("delete operation failed in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to delete attendee {id}, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to get all attendees for PCRB plan# {planNumber}"); | ||||
|  | ||||
|             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan#"); | ||||
|  | ||||
|             IEnumerable<PCRBAttendee>? attendees = null; | ||||
|  | ||||
|             if (!bypassCache) | ||||
|                 attendees = _cache.Get<IEnumerable<PCRBAttendee>>($"pcrbAttendees{planNumber}"); | ||||
|  | ||||
|             if (attendees is null) { | ||||
|                 string sql = $"select * from CCPCRBAttendee where PlanNumber={planNumber}"; | ||||
|  | ||||
|                 attendees = await _dalService.QueryAsync<PCRBAttendee>(sql); | ||||
|  | ||||
|                 _cache.Set($"pcrbAttendees{planNumber}", attendees, DateTimeOffset.Now.AddMinutes(15)); | ||||
|             } | ||||
|  | ||||
|             return attendees; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to get all attendees for PCRB plan# {planNumber}, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task CreatePCR3Document(PCR3Document document) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to create new PCR3 document"); | ||||
|  | ||||
|             if (document is null) throw new ArgumentNullException("document item cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append("insert into CCPCR3Document (PlanNumber, DocType) "); | ||||
|             queryBuilder.Append($"values ({document.PlanNumber}, '{document.DocType}')"); | ||||
|  | ||||
|             int rowsCreated = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsCreated <= 0) throw new Exception("unable to insert new PCR3 document in the database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to create new PCR3 document, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task UpdatePCR3Document(PCR3Document document) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to update a PCR3 document"); | ||||
|  | ||||
|             if (document is null) throw new ArgumentNullException("document cannot be null"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append($"update CCPCR3Document set DocNumbers='{document.DocNumbers}', "); | ||||
|             queryBuilder.Append($"Comment='{document.Comment.Replace("'", "''")}', ECNNumber={document.ECNNumber}, "); | ||||
|             queryBuilder.Append($"CompletedDate='{document.CompletedDate.ToString("yyyy-MM-dd HH:mm:ss")}', "); | ||||
|             queryBuilder.Append($"CompletedByID={document.CompletedByID} "); | ||||
|             queryBuilder.Append($"where ID={document.ID}"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsAffected <= 0) throw new Exception("update failed in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to update PCR3 document, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to get all PCR3 documents for PCRB plan# {planNumber}"); | ||||
|  | ||||
|             if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan#"); | ||||
|  | ||||
|             IEnumerable<PCR3Document>? documents = null; | ||||
|  | ||||
|             if (!bypassCache) | ||||
|                 documents = _cache.Get<IEnumerable<PCR3Document>>($"pcr3Documents{planNumber}"); | ||||
|  | ||||
|             if (documents is null) { | ||||
|                 string sql = $"select * from CCPCR3Document where PlanNumber={planNumber}"; | ||||
|  | ||||
|                 documents = await _dalService.QueryAsync<PCR3Document>(sql); | ||||
|  | ||||
|                 _cache.Set($"pcr3Documents{planNumber}", documents, DateTimeOffset.Now.AddMinutes(15)); | ||||
|             } | ||||
|  | ||||
|             return documents; | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to get all PCR3 documents for PCRB plan# {planNumber}, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task NotifyNewApprovals(PCRB pcrb) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to notify approvers"); | ||||
|  | ||||
|             if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|  | ||||
|             IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(pcrb.PlanNumber, true); | ||||
|  | ||||
|             List<Approval> approvalsNeedingNotification = approvals.Where(a => a.Step == pcrb.CurrentStep && | ||||
|                                                                                a.NotifyDate <= DateTimeUtilities.MIN_DT && | ||||
|                                                                                a.AssignedDate > DateTimeUtilities.MIN_DT).ToList(); | ||||
|  | ||||
|             HashSet<string> emailsAlreadySent = new(); | ||||
|             foreach (Approval approval in approvalsNeedingNotification) { | ||||
|                 User user = await _userService.GetUserByUserId(approval.UserID); | ||||
|  | ||||
|                 if (!emailsAlreadySent.Contains(user.Email.ToLower())) { | ||||
|                     emailsAlreadySent.Add(user.Email); | ||||
|  | ||||
|                     List<MailAddress> toAddresses = new(); | ||||
|                     toAddresses.Add(new MailAddress(user.Email.ToLower())); | ||||
|  | ||||
|                     List<MailAddress> ccAddresses = new(); | ||||
|  | ||||
|                     string subject = $"[New Task] Mesa Fab Approval - PCRB# {pcrb.PlanNumber} - {pcrb.Title}"; | ||||
|  | ||||
|                     StringBuilder bodyBuilder = new(); | ||||
|                     bodyBuilder.Append($"PCRB# {pcrb.PlanNumber} [{pcrb.Title}] PCR{approval.Step} is ready for your approval. "); | ||||
|                     bodyBuilder.Append($"The assigned role is {approval.SubRoleCategoryItem}. <br /> <br />"); | ||||
|                     bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{approval.IssueID} to view the PCRB."); | ||||
|  | ||||
|                     await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); | ||||
|  | ||||
|                     approval.NotifyDate = DateTime.Now; | ||||
|                     await _approvalService.UpdateApproval(approval); | ||||
|                 } | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to notify approvers, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task NotifyApprovers(PCRBNotification notification) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to send notification to approvers"); | ||||
|  | ||||
|             if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||
|             if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|             if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); | ||||
|  | ||||
|             IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(notification.PCRB.PlanNumber, true); | ||||
|  | ||||
|             HashSet<string> emailsAlreadySent = new(); | ||||
|             foreach (Approval approval in approvals) { | ||||
|                 User user = await _userService.GetUserByUserId(approval.UserID); | ||||
|  | ||||
|                 if (!emailsAlreadySent.Contains(user.Email)) { | ||||
|                     emailsAlreadySent.Add(user.Email); | ||||
|  | ||||
|                     List<MailAddress> toAddresses = new(); | ||||
|                     toAddresses.Add(new MailAddress(user.Email)); | ||||
|  | ||||
|                     List<MailAddress> ccAddresses = new(); | ||||
|  | ||||
|                     string subject = $"[Update] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}"; | ||||
|  | ||||
|                     StringBuilder bodyBuilder = new(); | ||||
|                     bodyBuilder.Append($"{notification.Message} <br /> <br />"); | ||||
|                     bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{approval.IssueID} to view the PCRB."); | ||||
|  | ||||
|                     await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); | ||||
|  | ||||
|                     approval.NotifyDate = DateTime.Now; | ||||
|                     await _approvalService.UpdateApproval(approval); | ||||
|                 } | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to send notification to originator, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task NotifyOriginator(PCRBNotification notification) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to send notification to originator"); | ||||
|  | ||||
|             if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||
|             if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|             if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); | ||||
|  | ||||
|             User user = await _userService.GetUserByUserId(notification.PCRB.OwnerID); | ||||
|  | ||||
|             List<MailAddress> toAddresses = new(); | ||||
|             toAddresses.Add(new MailAddress(user.Email)); | ||||
|  | ||||
|             List<MailAddress> ccAddresses = new(); | ||||
|  | ||||
|             string subject = $"[Update] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}"; | ||||
|  | ||||
|             StringBuilder bodyBuilder = new(); | ||||
|             bodyBuilder.Append($"{notification.Message} <br /> <br />"); | ||||
|             bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{notification.PCRB.PlanNumber} to view the PCRB."); | ||||
|  | ||||
|             await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to send notification to originator, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task NotifyResponsiblePerson(PCRBActionItemNotification notification) { | ||||
|         try { | ||||
|             _logger.LogInformation("Attempting to notify responsible person"); | ||||
|  | ||||
|             if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||
|             if (string.IsNullOrWhiteSpace(notification.Message)) | ||||
|                 throw new ArgumentException("message cannot be null or empty"); | ||||
|  | ||||
|             if (notification.ActionItem.ResponsiblePerson is null) | ||||
|                 notification.ActionItem.ResponsiblePerson = await _userService.GetUserByUserId(notification.ActionItem.ResponsiblePersonID); | ||||
|  | ||||
|             List<MailAddress> toAddresses = new(); | ||||
|             toAddresses.Add(new MailAddress(notification.ActionItem.ResponsiblePerson.Email)); | ||||
|  | ||||
|             List<MailAddress> ccAddresses = new(); | ||||
|  | ||||
|             string subject = $"[New Task] Mesa Fab Approval - PCRB# {notification.PCRB.PlanNumber} - {notification.PCRB.Title}"; | ||||
|  | ||||
|             StringBuilder bodyBuilder = new(); | ||||
|             bodyBuilder.Append($"{notification.Message} <br /> <br />"); | ||||
|             bodyBuilder.Append($"Click {_siteBaseUrl}/redirect?redirectPath=pcrb/{notification.PCRB.PlanNumber} to view the PCRB."); | ||||
|  | ||||
|             await _smtpService.SendEmail(toAddresses, ccAddresses, subject, bodyBuilder.ToString()); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to notify responsible person, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task SaveAttachmentInDb(IFormFile file, PCRBAttachment attachment) { | ||||
|         try { | ||||
|             _logger.LogInformation($"Attempting to save attachment to database"); | ||||
|  | ||||
|             if (file is null) throw new ArgumentNullException("File cannot be null"); | ||||
|             if (string.IsNullOrWhiteSpace(attachment.Path)) throw new ArgumentException("Path cannot be null or empty"); | ||||
|             if (attachment.PlanNumber <= 0) throw new ArgumentException($"{attachment.PlanNumber} is not a valid PCRB Plan#"); | ||||
|  | ||||
|             StringBuilder queryBuilder = new(); | ||||
|             queryBuilder.Append("insert into CCAttachment (PlanNumber, FileName, UploadDateTime, Path, UploadedByID, Title, "); | ||||
|             queryBuilder.Append($"Step) values ({attachment.PlanNumber}, '{file.FileName}', "); | ||||
|             queryBuilder.Append($"'{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{attachment.Path}', {attachment.UploadedByID}, "); | ||||
|             queryBuilder.Append($"'{attachment.Title}', {attachment.Step});"); | ||||
|  | ||||
|             int rowsAffected = await _dalService.ExecuteAsync(queryBuilder.ToString()); | ||||
|  | ||||
|             if (rowsAffected <= 0) | ||||
|                 throw new Exception("Unable to insert attachment in database"); | ||||
|         } catch (Exception ex) { | ||||
|             _logger.LogError($"Unable to save file to DB, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,9 +1,10 @@ | ||||
| using MesaFabApproval.API.Clients; | ||||
| using System.Net.Mail; | ||||
|  | ||||
| using MesaFabApproval.API.Clients; | ||||
| using MesaFabApproval.Models; | ||||
|  | ||||
| using Microsoft.IdentityModel.Tokens; | ||||
|  | ||||
| using System.Net.Mail; | ||||
|  | ||||
| namespace MesaFabApproval.API.Services; | ||||
|  | ||||
| public interface ISmtpService { | ||||
| @ -15,13 +16,12 @@ public class SmtpService : ISmtpService { | ||||
|     private readonly ISmtpClientWrapper _smtpClient; | ||||
|     private readonly bool _shouldSendEmail; | ||||
|  | ||||
|     public SmtpService(ILogger<SmtpService> logger, ISmtpClientWrapper smtpClient) { | ||||
|     public SmtpService(ILogger<SmtpService> logger, ISmtpClientWrapper smtpClient, AppSettings appSettings) { | ||||
|         _logger = logger ?? | ||||
|             throw new ArgumentNullException("ILogger not injected"); | ||||
|         _smtpClient = smtpClient ?? | ||||
|             throw new ArgumentNullException("SmtpClient not injected"); | ||||
|         if (!Boolean.TryParse(Environment.GetEnvironmentVariable("FabApprovalShouldSendEmail"), out _shouldSendEmail)) | ||||
|             throw new ArgumentNullException("FabApprovalShouldSendEmail environment variable not found"); | ||||
|         _shouldSendEmail = appSettings.ShouldSendEmail; | ||||
|     } | ||||
|  | ||||
|     public async Task<bool> SendEmail(IEnumerable<MailAddress> recipients, | ||||
| @ -32,7 +32,7 @@ public class SmtpService : ISmtpService { | ||||
|         if (ccRecipients is null) throw new ArgumentNullException("ccRecipients cannot be null!"); | ||||
|         if (subject.IsNullOrEmpty()) throw new ArgumentNullException("subject cannot be null or empty!"); | ||||
|         if (body.IsNullOrEmpty()) throw new ArgumentNullException("body cannot be null or empty!"); | ||||
|          | ||||
|  | ||||
|         return await Task.Run(() => { | ||||
|             int maxRetries = 3; | ||||
|             int backoffSeconds = 30; | ||||
| @ -76,4 +76,4 @@ public class SmtpService : ISmtpService { | ||||
|             return messageWasSent; | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| } | ||||
| @ -144,4 +144,4 @@ public class UserService : IUserService { | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
							
								
								
									
										31
									
								
								MesaFabApproval.API/Utilities/FileUtilities.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								MesaFabApproval.API/Utilities/FileUtilities.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| using Microsoft.AspNetCore.Components.Forms; | ||||
|  | ||||
| using NLog; | ||||
|  | ||||
| namespace MesaFabApproval.API.Utilities; | ||||
|  | ||||
| public class FileUtilities { | ||||
|     private static readonly Logger _logger = NLog.LogManager.GetCurrentClassLogger(); | ||||
|  | ||||
|     public static async Task SaveFileToFileSystem(IFormFile file, string path) { | ||||
|         try { | ||||
|             _logger.Info($"Attempting to save file to file system"); | ||||
|  | ||||
|             if (file is null) throw new ArgumentNullException("File cannot be null"); | ||||
|             if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Path cannot be null or empty"); | ||||
|  | ||||
|             if (File.Exists(path)) throw new Exception($"A file already exists with name {file.FileName}"); | ||||
|  | ||||
|             string? directoryPath = Path.GetDirectoryName(path); | ||||
|             if (!string.IsNullOrWhiteSpace(directoryPath)) | ||||
|                 Directory.CreateDirectory(directoryPath); | ||||
|  | ||||
|             using (Stream stream = File.Create(path)) { | ||||
|                 await file.CopyToAsync(stream); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             _logger.Error($"Unable to save file to file system, because {ex.Message}"); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,8 +0,0 @@ | ||||
| { | ||||
|   "Logging": { | ||||
|     "LogLevel": { | ||||
|       "Default": "Information", | ||||
|       "Microsoft.AspNetCore": "Warning" | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -1,9 +1,3 @@ | ||||
| { | ||||
|   "Logging": { | ||||
|     "LogLevel": { | ||||
|       "Default": "Information", | ||||
|       "Microsoft.AspNetCore": "Warning" | ||||
|     } | ||||
|   }, | ||||
|   "AllowedHosts": "*" | ||||
| } | ||||
|  | ||||
| @ -1,32 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8" ?> | ||||
| <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" | ||||
|       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|       autoReload="true"> | ||||
|  | ||||
|     <extensions> | ||||
|         <add assembly="NLog.Web.AspNetCore"/> | ||||
|     </extensions> | ||||
|      | ||||
|     <targets> | ||||
|         <target name="asyncFile" xsi:type="AsyncWrapper"> | ||||
|             <target  | ||||
|                 name="appLog"  | ||||
|                 xsi:type="File"  | ||||
|                 fileName="d:\logs\MesaProveInApi\log.txt" | ||||
|                 archiveFilename="d:\logs\MesaProveInApi\archive\log-${shortdate}.txt" | ||||
|                 maxArchiveFiles="15" | ||||
|                 archiveEvery="Day" | ||||
|             /> | ||||
|             <target  | ||||
|                 name="consoleLog" | ||||
|                 xsi:type="Console" | ||||
|             /> | ||||
|         </target> | ||||
|     </targets> | ||||
|  | ||||
|     <rules> | ||||
|         <logger name="Microsoft.*" finalMinLevel="Warn" /> | ||||
|         <logger name="System.Net.Http.HttpClient.*" finalMinLevel="Warn" /> | ||||
|         <logger name="*" minlevel="Debug" writeTo="consoleLog, appLog"/> | ||||
|     </rules> | ||||
| </nlog> | ||||
| @ -8,16 +8,16 @@ | ||||
|     </extensions> | ||||
|      | ||||
|     <targets> | ||||
|         <target name="asyncFile" xsi:type="AsyncWrapper"> | ||||
|             <target  | ||||
|                 name="appLog"  | ||||
|                 xsi:type="File"  | ||||
|         <target name="asyncLog" xsi:type="AsyncWrapper"> | ||||
|             <target | ||||
|                 name="appLog" | ||||
|                 xsi:type="File" | ||||
|                 fileName="d:\logs\MesaFabApproval.API\log.txt" | ||||
|                 archiveFilename="d:\logs\MesaFabApproval.API\archive\log-${shortdate}.txt" | ||||
|                 maxArchiveFiles="30" | ||||
|                 archiveEvery="Day" | ||||
|             /> | ||||
|             <target  | ||||
|             <target | ||||
|                 name="consoleLog" | ||||
|                 xsi:type="Console" | ||||
|             /> | ||||
| @ -26,6 +26,7 @@ | ||||
|  | ||||
|     <rules> | ||||
|         <logger name="Microsoft.*" finalMinLevel="Warn" /> | ||||
|         <logger name="Microsoft.AspNetCore.HttpLogging.*" finalMinLevel="Info" /> | ||||
|         <logger name="System.Net.Http.HttpClient.*" finalMinLevel="Warn" /> | ||||
|         <logger name="*" minlevel="Info" writeTo="consoleLog, appLog" /> | ||||
|     </rules> | ||||
|  | ||||
							
								
								
									
										1
									
								
								MesaFabApproval.Client/.vscode/format-report.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								MesaFabApproval.Client/.vscode/format-report.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| [] | ||||
							
								
								
									
										30
									
								
								MesaFabApproval.Client/.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								MesaFabApproval.Client/.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| { | ||||
|     // Use IntelliSense to learn about possible attributes. | ||||
|     // Hover to view descriptions of existing attributes. | ||||
|     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||
|     "version": "0.2.0", | ||||
|     "configurations": [ | ||||
|         { | ||||
|             "name": ".NET Core Launch (console)", | ||||
|             "type": "coreclr", | ||||
|             "request": "launch", | ||||
|             "preLaunchTask": "build", | ||||
|             "program": "${workspaceFolder}/bin/Debug/net8.0/MesaFabApproval.API.dll", | ||||
|             "args": [], | ||||
|             "cwd": "${workspaceFolder}", | ||||
|             "console": "integratedTerminal", | ||||
|             "stopAtEntry": false | ||||
|         }, | ||||
|         { | ||||
|             "name": ".NET Core Attach", | ||||
|             "type": "coreclr", | ||||
|             "request": "attach" | ||||
|         }, | ||||
|         { | ||||
|             "type": "node", | ||||
|             "request": "launch", | ||||
|             "name": "node Launch Current Opened File", | ||||
|             "program": "${file}" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										6
									
								
								MesaFabApproval.Client/.vscode/mklink.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								MesaFabApproval.Client/.vscode/mklink.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| # mklink | ||||
|  | ||||
| ```bash 1731705389065 = 638673021890650000 = Fri Nov 15 2024 14:16:28 GMT-0700 (Mountain Standard Time) | ||||
| mklink /J "L:\DevOps\Mesa_FI\MesaFabApproval\MesaFabApproval.API\.vscode\.UserSecrets" "C:\Users\phares\AppData\Roaming\Microsoft\UserSecrets\0b98e1f2-95ed-4edd-8149-58cce51ca059" | ||||
| dotnet run --urls=https://localhost:7114/ -C Release | ||||
| ``` | ||||
							
								
								
									
										424
									
								
								MesaFabApproval.Client/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										424
									
								
								MesaFabApproval.Client/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,424 @@ | ||||
| { | ||||
|   "[markdown]": { | ||||
|     "editor.wordWrap": "off" | ||||
|   }, | ||||
|   "files.exclude": { | ||||
|     "**/.git": false, | ||||
|     "**/node_modules": true | ||||
|   }, | ||||
|   "files.watcherExclude": { | ||||
|     "**/node_modules": true | ||||
|   }, | ||||
|   "cSpell.words": [ | ||||
|     "abutton", | ||||
|     "accessibilities", | ||||
|     "accodingly", | ||||
|     "acknowledgmentby", | ||||
|     "Acks", | ||||
|     "actionsheet", | ||||
|     "Additonal", | ||||
|     "Addtional", | ||||
|     "againm", | ||||
|     "agendaview", | ||||
|     "Antlr", | ||||
|     "Appover", | ||||
|     "Appprrovers", | ||||
|     "Approvalog", | ||||
|     "Aprovers", | ||||
|     "Aproverslist", | ||||
|     "asax", | ||||
|     "aspnetmvc", | ||||
|     "Assignedto", | ||||
|     "Atachments", | ||||
|     "Attachemnt", | ||||
|     "Attachemnts", | ||||
|     "Attchment", | ||||
|     "auditee", | ||||
|     "Auditee", | ||||
|     "Auditees", | ||||
|     "automaically", | ||||
|     "Autthorized", | ||||
|     "beacuase", | ||||
|     "beforeunload", | ||||
|     "Belguim", | ||||
|     "bfound", | ||||
|     "bgcolor", | ||||
|     "Bies", | ||||
|     "binded", | ||||
|     "blackbackground", | ||||
|     "blackhover", | ||||
|     "blackpressed", | ||||
|     "blueenergy", | ||||
|     "blueopal", | ||||
|     "buttongroup", | ||||
|     "BYMRB", | ||||
|     "bytesgot", | ||||
|     "CAID", | ||||
|     "casection", | ||||
|     "CAXXXX", | ||||
|     "CCPCR", | ||||
|     "CCPCRB", | ||||
|     "cellspacing", | ||||
|     "Cheeso's", | ||||
|     "chkbx", | ||||
|     "Clib", | ||||
|     "colorpicker", | ||||
|     "columnmenu", | ||||
|     "columnsreorder", | ||||
|     "columnsresize", | ||||
|     "comming", | ||||
|     "Containmen", | ||||
|     "Copmments", | ||||
|     "correctiv", | ||||
|     "Correctivet", | ||||
|     "Creat", | ||||
|     "currentd", | ||||
|     "Cyle", | ||||
|     "dadada", | ||||
|     "darkbluehover", | ||||
|     "darkbluepressed", | ||||
|     "darkred", | ||||
|     "datafields", | ||||
|     "datasource", | ||||
|     "dataviz", | ||||
|     "datepicker", | ||||
|     "datetimepicker", | ||||
|     "dayview", | ||||
|     "Deletet", | ||||
|     "Delgation", | ||||
|     "DENITED", | ||||
|     "Deparmtent", | ||||
|     "departmentids", | ||||
|     "Descirption", | ||||
|     "devprog", | ||||
|     "dfeffc", | ||||
|     "Disp", | ||||
|     "Dispo", | ||||
|     "Dispoitio", | ||||
|     "Dispos", | ||||
|     "Dispositon", | ||||
|     "Dispostion", | ||||
|     "Dispostions", | ||||
|     "dispotypevalidation", | ||||
|     "Docbase", | ||||
|     "Documentum", | ||||
|     "Documetum", | ||||
|     "dont", | ||||
|     "downlaoded", | ||||
|     "draganddrop", | ||||
|     "dragcancel", | ||||
|     "dropdownlist", | ||||
|     "Eamils", | ||||
|     "ECNPCRB", | ||||
|     "Ecns", | ||||
|     "edmx", | ||||
|     "EECN", | ||||
|     "emai", | ||||
|     "emailparams", | ||||
|     "Emergrncy", | ||||
|     "energyblue", | ||||
|     "Eran", | ||||
|     "Esql", | ||||
|     "ETECN", | ||||
|     "EXCELOPENXML", | ||||
|     "existinglocation", | ||||
|     "Expando", | ||||
|     "extrafield", | ||||
|     "fadc", | ||||
|     "fbec", | ||||
|     "fcfdfd", | ||||
|     "fdff", | ||||
|     "fece", | ||||
|     "feeebd", | ||||
|     "ffdc", | ||||
|     "ffdd", | ||||
|     "fieldset", | ||||
|     "FILIPE", | ||||
|     "filtermenu", | ||||
|     "Fldr", | ||||
|     "flintstone", | ||||
|     "FLOWLOCS", | ||||
|     "FMEA", | ||||
|     "ftplib", | ||||
|     "FTPSPN", | ||||
|     "GETDATE", | ||||
|     "gitea", | ||||
|     "globaldocudms", | ||||
|     "glyphicons", | ||||
|     "groupable", | ||||
|     "Guids", | ||||
|     "halflings", | ||||
|     "Hexsize", | ||||
|     "highcontrast", | ||||
|     "Hmac", | ||||
|     "holdsteps", | ||||
|     "hostspecific", | ||||
|     "icenium", | ||||
|     "IECN", | ||||
|     "imagebrowser", | ||||
|     "IMRB", | ||||
|     "Infineon", | ||||
|     "Insertd", | ||||
|     "inverseicons", | ||||
|     "IPCRB", | ||||
|     "ISADMIN", | ||||
|     "islast", | ||||
|     "ISNULL", | ||||
|     "ITAR", | ||||
|     "jquery", | ||||
|     "jqueryval", | ||||
|     "jqwidgets", | ||||
|     "jqxbuttongroup", | ||||
|     "jqxbuttons", | ||||
|     "jqxcalendar", | ||||
|     "jqxchart", | ||||
|     "jqxcheckbox", | ||||
|     "jqxcolorpicker", | ||||
|     "jqxcombobox", | ||||
|     "jqxcore", | ||||
|     "jqxdata", | ||||
|     "jqxdatatable", | ||||
|     "jqxdatetimeinput", | ||||
|     "jqxdocking", | ||||
|     "jqxdockpanel", | ||||
|     "jqxdragdrop", | ||||
|     "jqxdropdownbutton", | ||||
|     "jqxdropdownlist", | ||||
|     "jqxexpander", | ||||
|     "jqxgauge", | ||||
|     "jqxgrid", | ||||
|     "jqxinput", | ||||
|     "jqxknockout", | ||||
|     "jqxlistbox", | ||||
|     "jqxlistmenu", | ||||
|     "jqxmaskedinput", | ||||
|     "jqxmenu", | ||||
|     "jqxnavigationbar", | ||||
|     "jqxnumberinput", | ||||
|     "jqxpanel", | ||||
|     "jqxpasswordinput", | ||||
|     "jqxprogressbar", | ||||
|     "jqxradiobutton", | ||||
|     "jqxrangeselector", | ||||
|     "jqxrating", | ||||
|     "jqxresponse", | ||||
|     "jqxscrollbar", | ||||
|     "jqxscrollview", | ||||
|     "jqxslider", | ||||
|     "jqxsplitter", | ||||
|     "jqxswitchbutton", | ||||
|     "jqxtabs", | ||||
|     "jqxtooltip", | ||||
|     "jqxtouch", | ||||
|     "jqxtree", | ||||
|     "jqxtreegrid", | ||||
|     "jqxtreemap", | ||||
|     "jqxvalidator", | ||||
|     "jqxwindow", | ||||
|     "kendogridcustom", | ||||
|     "kendoui", | ||||
|     "labelelement", | ||||
|     "labelledby", | ||||
|     "Leanred", | ||||
|     "lightgray", | ||||
|     "linkbutton", | ||||
|     "Linq", | ||||
|     "Listdiv", | ||||
|     "listview", | ||||
|     "Lnks", | ||||
|     "localfilename", | ||||
|     "loclist", | ||||
|     "logis", | ||||
|     "logtext", | ||||
|     "loopmis", | ||||
|     "lotdispo", | ||||
|     "LOTDISPSITION", | ||||
|     "Lotfile", | ||||
|     "lotlist", | ||||
|     "lotstatusoption", | ||||
|     "LTRIM", | ||||
|     "MADUREIRA", | ||||
|     "mailrelay", | ||||
|     "MDTM", | ||||
|     "meego", | ||||
|     "meetingid", | ||||
|     "menubutton", | ||||
|     "mesafi", | ||||
|     "metroblack", | ||||
|     "metrodark", | ||||
|     "miliseconds", | ||||
|     "modalview", | ||||
|     "modernizr", | ||||
|     "Modernizr", | ||||
|     "monthview", | ||||
|     "MRBIs", | ||||
|     "Mrbs", | ||||
|     "msecs", | ||||
|     "multipleextended", | ||||
|     "Navigatable", | ||||
|     "nbsp", | ||||
|     "newbase", | ||||
|     "newchange", | ||||
|     "newdi", | ||||
|     "newfilename", | ||||
|     "newsource", | ||||
|     "Newtonsoft", | ||||
|     "notications", | ||||
|     "Notifcation", | ||||
|     "Notifyf", | ||||
|     "NTLM", | ||||
|     "Nullcc", | ||||
|     "numerictextbox", | ||||
|     "objdata", | ||||
|     "OCAP", | ||||
|     "occured", | ||||
|     "odata", | ||||
|     "oldfilename", | ||||
|     "OLHOLD", | ||||
|     "onclick", | ||||
|     "onmousemove", | ||||
|     "OPDESC", | ||||
|     "OPENQUERY", | ||||
|     "Oper", | ||||
|     "operationslist", | ||||
|     "Orginator", | ||||
|     "Originatorname", | ||||
|     "Ouellette", | ||||
|     "Owin", | ||||
|     "pageable", | ||||
|     "Pageable", | ||||
|     "panelbar", | ||||
|     "parentid", | ||||
|     "parminput", | ||||
|     "parms", | ||||
|     "Parms", | ||||
|     "particula", | ||||
|     "pasv", | ||||
|     "PASV", | ||||
|     "PATINDEX", | ||||
|     "PCRB", | ||||
|     "PCRBID", | ||||
|     "pcrvalues", | ||||
|     "pdbonly", | ||||
|     "Preventitive", | ||||
|     "preventivet", | ||||
|     "Prevetative", | ||||
|     "proces", | ||||
|     "Processedl", | ||||
|     "procs", | ||||
|     "productfamilies", | ||||
|     "progess", | ||||
|     "progressbar", | ||||
|     "qrcode", | ||||
|     "Quanityt", | ||||
|     "rangebar", | ||||
|     "Recep", | ||||
|     "Recepient", | ||||
|     "recieved", | ||||
|     "recordlock", | ||||
|     "remotefilename", | ||||
|     "reorderable", | ||||
|     "reportform", | ||||
|     "reportslist", | ||||
|     "reportslistdiv", | ||||
|     "Reqquired", | ||||
|     "Reqs", | ||||
|     "Requiest", | ||||
|     "Responsibles", | ||||
|     "RETR", | ||||
|     "Revisioing", | ||||
|     "Revisioned", | ||||
|     "Revison", | ||||
|     "rgba", | ||||
|     "rkotian", | ||||
|     "RNFR", | ||||
|     "RNTO", | ||||
|     "Roless", | ||||
|     "roundbg", | ||||
|     "RTRIM", | ||||
|     "SAMDB", | ||||
|     "scroller", | ||||
|     "scrollview", | ||||
|     "seleced", | ||||
|     "selectionlog", | ||||
|     "Selectpart", | ||||
|     "sess", | ||||
|     "Sfisharepoint", | ||||
|     "shinyblack", | ||||
|     "showpassword", | ||||
|     "SIGNON", | ||||
|     "simpleparser", | ||||
|     "slddrw", | ||||
|     "sldprt", | ||||
|     "sortasc", | ||||
|     "sortascbutton", | ||||
|     "sortdesc", | ||||
|     "sortdescbutton", | ||||
|     "sortremove", | ||||
|     "sparkline", | ||||
|     "splitview", | ||||
|     "SPNMRB", | ||||
|     "SPNPDB", | ||||
|     "SSRS", | ||||
|     "Sssign", | ||||
|     "Staus", | ||||
|     "stylesheet", | ||||
|     "Submited", | ||||
|     "subrole", | ||||
|     "subroles", | ||||
|     "Succefully", | ||||
|     "Succesfully", | ||||
|     "sucessfully", | ||||
|     "SURP", | ||||
|     "Swashbuckle", | ||||
|     "SWRN", | ||||
|     "tabindex", | ||||
|     "tabstrip", | ||||
|     "Tahoma", | ||||
|     "taskcompleted", | ||||
|     "Tasklist", | ||||
|     "Taveler", | ||||
|     "TECN", | ||||
|     "TECNs", | ||||
|     "TEMIRWAP", | ||||
|     "tempecd", | ||||
|     "tempimplement", | ||||
|     "templabel", | ||||
|     "tempvalue", | ||||
|     "TEMSA", | ||||
|     "timepicker", | ||||
|     "Tobe", | ||||
|     "Toplevel", | ||||
|     "Totrav", | ||||
|     "trainingby", | ||||
|     "Traininglist", | ||||
|     "traininglistdiv", | ||||
|     "transanction", | ||||
|     "Trav", | ||||
|     "Traveller", | ||||
|     "Traverler", | ||||
|     "TRAVLELER", | ||||
|     "Travler", | ||||
|     "TREEVIEW", | ||||
|     "trigerred", | ||||
|     "ttinclude", | ||||
|     "Uhandled", | ||||
|     "Updat", | ||||
|     "Uplaod", | ||||
|     "Upto", | ||||
|     "userevents", | ||||
|     "userids", | ||||
|     "userlist", | ||||
|     "Validatable", | ||||
|     "valueelement", | ||||
|     "Variabls", | ||||
|     "Verdana", | ||||
|     "vgrid", | ||||
|     "viewmodel", | ||||
|     "vsdoc", | ||||
|     "whethere", | ||||
|     "windowsphone", | ||||
|     "Winsock", | ||||
|     "worlflow" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										121
									
								
								MesaFabApproval.Client/.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								MesaFabApproval.Client/.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,121 @@ | ||||
| { | ||||
|     "version": "2.0.0", | ||||
|     "tasks": [ | ||||
|         { | ||||
|             "label": "User Secrets Init", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "user-secrets", | ||||
|                 "-p", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client.csproj", | ||||
|                 "init" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Format", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "--report", | ||||
|                 ".vscode", | ||||
|                 "--verbosity", | ||||
|                 "detailed", | ||||
|                 "--severity", | ||||
|                 "warn" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Format-Whitespaces", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "whitespace" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "build", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "testDebug", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "testRelease", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "test", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary", | ||||
|                 "-c", | ||||
|                 "Release" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "publish", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "watch", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "watch", | ||||
|                 "run", | ||||
|                 "--project", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client.csproj" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Publish AOT", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "publish", | ||||
|                 "-r", | ||||
|                 "win-x64", | ||||
|                 "-c", | ||||
|                 "Release", | ||||
|                 "-p:PublishAot=true", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Client.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										8
									
								
								MesaFabApproval.Client/GlobalSuppressions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								MesaFabApproval.Client/GlobalSuppressions.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| // This file is used by Code Analysis to maintain SuppressMessage | ||||
| // attributes that are applied to this project. | ||||
| // Project-level suppressions either have no target or are given | ||||
| // a specific target and scoped to a namespace, type, member, etc. | ||||
|  | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
|  | ||||
| [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>", Scope = "member", Target = "~M:MesaFabApproval.Client.Services.AuthenticationService.AttemptLocalUserAuth~System.Threading.Tasks.Task{System.Security.Claims.ClaimsPrincipal}")] | ||||
| @ -1,13 +1,20 @@ | ||||
| @inherits LayoutComponentBase | ||||
| @using System.Text.Json | ||||
| @using System.Text | ||||
| @inherits LayoutComponentBase | ||||
|  | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
| @inject IAuthenticationService authenticationService | ||||
| @inject IConfiguration Configuration | ||||
| @inject IMemoryCache cache | ||||
| @inject IJSRuntime jsRuntime | ||||
| @inject IHttpClientFactory httpClientFactory | ||||
| @inject ISnackbar snackbar | ||||
| @inject NavigationManager navManager | ||||
|  | ||||
| <MudThemeProvider /> | ||||
| <MudDialogProvider /> | ||||
| <MudSnackbarProvider /> | ||||
| <MudPopoverProvider /> | ||||
|  | ||||
| <div style="height: 100vh;"> | ||||
|     <MudLayout> | ||||
| @ -27,24 +34,27 @@ | ||||
|         <MudDrawer @bind-Open="_drawerOpen" ClipMode="DrawerClipMode.Always" Elevation="2"> | ||||
|             <MudNavMenu Color="Color.Info" Bordered="true" Class="d-flex flex-column justify-center p-1 gap-1"> | ||||
|                 <MudButton Variant="Variant.Filled" | ||||
|                            Color="Color.Tertiary" | ||||
|                            Href="@Configuration["OldFabApprovalUrl"]" | ||||
|                            Target="_blank" | ||||
|                            StartIcon="@Icons.Material.Filled.Home"> | ||||
|                             Color="Color.Tertiary" | ||||
|                             Href="@Configuration["OldFabApprovalUrl"]" | ||||
|                             Target="_blank" | ||||
|                             StartIcon="@Icons.Material.Filled.Home"> | ||||
|                     Return to Main Site | ||||
|                 </MudButton> | ||||
|  | ||||
|                 <MudDivider Class="my-1" /> | ||||
|                 @if (authStateProvider.CurrentUser is not null) { | ||||
|                     <MudNavGroup Title="Create New"> | ||||
|                         <MudNavLink OnClick="@(() => GoTo("mrb/new"))">Create New MRB</MudNavLink> | ||||
|                         <MudNavLink OnClick="@(() => GoTo("pcrb/new"))">Create New PCRB</MudNavLink> | ||||
|                     </MudNavGroup> | ||||
|                     <MudNavLink OnClick="@(() => GoTo(""))" Icon="@Icons.Material.Filled.Dashboard">Dashboard</MudNavLink> | ||||
|                     <MudNavLink OnClick="@(() => GoTo("mrb/all"))" Icon="@Icons.Material.Filled.Ballot">MRB</MudNavLink> | ||||
|                     <MudNavLink OnClick="@(() => GoTo("mrb/all"))" Icon="@Icons.Material.Filled.Ballot">MRB List</MudNavLink> | ||||
|                     <MudNavLink OnClick="@(() => GoTo("pcrb/all"))" Icon="@Icons.Material.Filled.Ballot">PCRB List</MudNavLink> | ||||
|                 } | ||||
|             </MudNavMenu> | ||||
|         </MudDrawer> | ||||
|         <div style="display: flex; flex-flow: column; height: 100%;"> | ||||
|             <MudMainContent Style="@($"background:{Colors.Grey.Lighten2}; flex-grow: 1;")"> | ||||
|             <MudMainContent Style="@($"background:#E0E0E0; flex-grow: 1;")"> | ||||
|                 @Body | ||||
|             </MudMainContent> | ||||
|         </div> | ||||
| @ -67,4 +77,48 @@ | ||||
|         cache.Set("redirectUrl", page); | ||||
|         navManager.NavigateTo(page); | ||||
|     } | ||||
|  | ||||
|     private async Task GoToExternal(string url, string content) { | ||||
|         IJSObjectReference windowModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/OpenInNewWindow.js"); | ||||
|         await windowModule.InvokeAsync<object>("OpenInNewWindow", url, content); | ||||
|     } | ||||
|  | ||||
|     private async Task GoToOldSite() { | ||||
|         try { | ||||
|             User? currentUser = authStateProvider.CurrentUser; | ||||
|  | ||||
|             AuthTokens? authTokens = await authenticationService.GetAuthTokens(); | ||||
|  | ||||
|             if (currentUser is null || authTokens is null) { | ||||
|                 await authStateProvider.Logout(); | ||||
|                 navManager.NavigateTo("login"); | ||||
|                 return; | ||||
|             } | ||||
|              | ||||
|             AuthAttempt authAttempt = new() { | ||||
|                     LoginID = currentUser.LoginID, | ||||
|                     AuthTokens = authTokens | ||||
|                 }; | ||||
|  | ||||
|             HttpClient httpClient = httpClientFactory.CreateClient("OldSite"); | ||||
|  | ||||
|             HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "Account/ExternalAuthSetup"); | ||||
|  | ||||
|             request.Content = new StringContent(JsonSerializer.Serialize(authAttempt), | ||||
|                                                 Encoding.UTF8, | ||||
|                                                 "application/json"); | ||||
|  | ||||
|             HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request); | ||||
|  | ||||
|             if (httpResponseMessage.IsSuccessStatusCode) { | ||||
|                 snackbar.Add("Old site auth setup successful", Severity.Success); | ||||
|             } else { | ||||
|                 snackbar.Add($"Old site auth setup failed, because {httpResponseMessage.ReasonPhrase}", Severity.Error); | ||||
|             } | ||||
|  | ||||
|             await GoToExternal($"{Configuration["OldFabApprovalUrl"]}", ""); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add($"Unable to go to old site, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -6,17 +6,24 @@ | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|     <ItemGroup> | ||||
|         <Watch Include="**\*.razor" /> | ||||
|     </ItemGroup> | ||||
|  | ||||
|     <ItemGroup> | ||||
|       <Watch Remove="Pages\Components\PCRBApproverForm.razor" /> | ||||
|     </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Blazored.SessionStorage" Version="2.4.0" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.6" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.6" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.6" PrivateAssets="all" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.8" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.6" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.0" /> | ||||
|     <PackageReference Include="MudBlazor" Version="6.20.0" /> | ||||
|     <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.1" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="8.0.8" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" /> | ||||
|     <PackageReference Include="MudBlazor" Version="7.6.0" /> | ||||
|     <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|     <ItemGroup> | ||||
|  | ||||
| @ -25,7 +25,7 @@ | ||||
|             } | ||||
|  | ||||
|             if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("redirectPath", out var redirectPath)) { | ||||
|                 _redirectPath = System.Net.WebUtility.UrlDecode(redirectPath); | ||||
|                 _redirectPath = redirectPath.ToString(); | ||||
|             } | ||||
|  | ||||
|             if (!string.IsNullOrWhiteSpace(_jwt) && !string.IsNullOrWhiteSpace(_refreshToken)) { | ||||
| @ -35,11 +35,12 @@ | ||||
|  | ||||
|                 string loginId = userService.GetLoginIdFromClaimsPrincipal(principal); | ||||
|  | ||||
|                 await authService.ClearCurrentUser(); | ||||
|                 await authService.ClearTokens(); | ||||
|  | ||||
|                 await authService.SetLoginId(loginId); | ||||
|                 await authService.SetTokens(_jwt, _refreshToken); | ||||
|  | ||||
|                 User? user = await userService.GetUserByLoginId(loginId); | ||||
|                 await authService.SetCurrentUser(user); | ||||
|                 await authService.SetCurrentUser(null); | ||||
|  | ||||
|                 await authStateProvider.StateHasChanged(principal); | ||||
|             } | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         <MudPaper Class="p-2"> | ||||
|         <MudPaper Class="m-2 p-2"> | ||||
|             <MudForm @bind-Errors="@errors"> | ||||
|                 <MudTextField T="string" | ||||
|                     Label="Comments" | ||||
| @ -46,7 +46,7 @@ | ||||
|  | ||||
|     private bool processing = false; | ||||
|  | ||||
|     protected override async Task OnParametersSetAsync() { | ||||
|     protected override void OnParametersSet() { | ||||
|         comments = string.Empty; | ||||
|     } | ||||
|  | ||||
| @ -64,6 +64,6 @@ | ||||
|     } | ||||
|  | ||||
|     private void Cancel() { | ||||
|         MudDialog.Cancel(); | ||||
|         MudDialog.Close(DialogResult.Cancel()); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,115 @@ | ||||
| @inject ISnackbar snackbar | ||||
| @inject IMRBService mrbService | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         <MudPaper Class="m-2 p-2"> | ||||
|             <MudForm @bind-Errors="@errors"> | ||||
|                 <MudTextField T="string" | ||||
|                     Label="Comments" | ||||
|                     Required="true" | ||||
|                     RequiredError="You must provide a comment" | ||||
|                     @bind-Value="@comments" | ||||
|                     @bind-Text="@comments" | ||||
|                     Immediate="true" | ||||
|                     AutoGrow | ||||
|                     AutoFocus/> | ||||
|                 <MudFileUpload T="IReadOnlyList<IBrowserFile>" OnFilesChanged="AddAttachments"> | ||||
|                     <ActivatorContent> | ||||
|                         <MudButton Variant="Variant.Filled" | ||||
|                                    Color="Color.Tertiary" | ||||
|                                    style="margin: auto;" | ||||
|                                    StartIcon="@Icons.Material.Filled.AttachFile"> | ||||
|                             @if (attachmentUploadInProcess) { | ||||
|                                 <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                                 <MudText>Processing</MudText> | ||||
|                             } else { | ||||
|                                 <MudText>Upload Supporting Document</MudText> | ||||
|                             } | ||||
|                         </MudButton> | ||||
|                     </ActivatorContent> | ||||
|                 </MudFileUpload> | ||||
|             </MudForm> | ||||
|         </MudPaper> | ||||
|     </DialogContent> | ||||
|     <DialogActions> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Color="Color.Tertiary" | ||||
|                    Class="m1-auto" | ||||
|                    OnClick=SubmitComments> | ||||
|             @if (processing) { | ||||
|                 <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                 <MudText>Processing</MudText> | ||||
|             } else { | ||||
|                 <MudText>Ok</MudText> | ||||
|             } | ||||
|         </MudButton> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Class="grey text-black m1-auto" | ||||
|                    OnClick=Cancel> | ||||
|             Cancel | ||||
|         </MudButton> | ||||
|     </DialogActions> | ||||
| </MudDialog> | ||||
|  | ||||
| @code { | ||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } | ||||
|  | ||||
|     [Parameter] | ||||
|     public string comments { get; set; } = ""; | ||||
|  | ||||
|     [Parameter] | ||||
|     public int actionId { get; set; } = 0; | ||||
|  | ||||
|     private string[] errors = { }; | ||||
|  | ||||
|     private bool processing = false; | ||||
|     private bool attachmentUploadInProcess = false; | ||||
|  | ||||
|     protected override void OnParametersSet() { | ||||
|         comments = string.Empty; | ||||
|     } | ||||
|  | ||||
|     private void SubmitComments() { | ||||
|         processing = true; | ||||
|         try { | ||||
|             if (string.IsNullOrWhiteSpace(comments) || comments.Length < 5) | ||||
|                 throw new Exception("Comments must be at least five characters long"); | ||||
|  | ||||
|             MudDialog.Close(DialogResult.Ok(comments)); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
|         } | ||||
|         processing = false; | ||||
|     } | ||||
|  | ||||
|     private void Cancel() { | ||||
|         MudDialog.Close(DialogResult.Cancel()); | ||||
|     } | ||||
|  | ||||
|     private async Task AddAttachments(InputFileChangeEventArgs args) { | ||||
|         attachmentUploadInProcess = true; | ||||
|         try { | ||||
|             if (actionId <= 0) | ||||
|                 throw new Exception($"{actionId} is not a valid MRB action ID"); | ||||
|  | ||||
|             IReadOnlyList<IBrowserFile> attachments = args.GetMultipleFiles(); | ||||
|  | ||||
|             if (authStateProvider.CurrentUser is not null) { | ||||
|                 await mrbService.UploadActionAttachments(attachments, actionId); | ||||
|  | ||||
|                 await mrbService.GetAllActionAttachmentsForMRB(actionId, true); | ||||
|  | ||||
|                 attachmentUploadInProcess = false; | ||||
|  | ||||
|                 snackbar.Add("Attachments successfully uploaded", Severity.Success); | ||||
|  | ||||
|                 StateHasChanged(); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             attachmentUploadInProcess = false; | ||||
|             snackbar.Add($"Unable to upload attachments, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,48 +1,110 @@ | ||||
| @inject IMRBService mrbService | ||||
| @inject ISnackbar snackbar | ||||
| @inject ICustomerService customerService | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         <MudPaper Class="p-2"> | ||||
|         <MudPaper Class="m-2 p-2"> | ||||
|             <MudForm @bind-Errors="@errors"> | ||||
|                 <MudSelect T="string" | ||||
|                            Label="Action" | ||||
|                            Disabled="@(!string.IsNullOrWhiteSpace(mrbAction.Action))" | ||||
|                            Required="true" | ||||
|                            RequiredError="You must select an action!" | ||||
|                            @bind-Value="@mrbAction.Action" | ||||
|                            Text="@mrbAction.Action"> | ||||
|                     <MudSelectItem Value="@("Block")" /> | ||||
|                     <MudSelectItem Value="@("Convert")" /> | ||||
|                     <MudSelectItem Value="@("Other")" /> | ||||
|                     <MudSelectItem Value="@("Recall")" /> | ||||
|                     <MudSelectItem Value="@("Scrap")" /> | ||||
|                     <MudSelectItem Value="@("Unblock")" /> | ||||
|                     <MudSelectItem Value="@("Waiver")" /> | ||||
|                 </MudSelect> | ||||
|                 <MudTextField T="string" | ||||
|                               Label="Customer / Vendor" | ||||
|                               Required="true" | ||||
|                               RequiredError="Customer / Vendor required!" | ||||
|                               @bind-Value="@mrbAction.Customer" | ||||
|                               Text="@mrbAction.Customer" /> | ||||
|                 @if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) { | ||||
|                     <MudSelect T="string" | ||||
|                                Label="Convert From Customer" | ||||
|                                Required | ||||
|                                Variant="Variant.Outlined" | ||||
|                                AnchorOrigin="Origin.BottomCenter" | ||||
|                                @bind-Value=convertFromCustomer | ||||
|                                Text="@convertFromCustomer"> | ||||
|                         @foreach (string customer in customerNames) { | ||||
|                             <MudSelectItem Value="@(customer)" /> | ||||
|                         } | ||||
|                     </MudSelect> | ||||
|                     <MudTextField @bind-Value="@convertFromPart" | ||||
|                                   Label="Convert From Part Number" | ||||
|                                   Required | ||||
|                                   RequiredError="Part number required!" | ||||
|                                   Text="@convertFromPart" /> | ||||
|                     <MudSelect T="string" | ||||
|                                Label="Convert To Customer" | ||||
|                                Required | ||||
|                                Variant="Variant.Outlined" | ||||
|                                AnchorOrigin="Origin.BottomCenter" | ||||
|                                @bind-Value=convertToCustomer | ||||
|                                Text="@convertToCustomer"> | ||||
|                         @foreach (string customer in customerNames) { | ||||
|                             <MudSelectItem Value="@(customer)" /> | ||||
|                         } | ||||
|                     </MudSelect> | ||||
|                     <MudTextField @bind-Value="@convertToPart" | ||||
|                                   Label="Convert To Part Number" | ||||
|                                   Required | ||||
|                                   RequiredError="Part number required!" | ||||
|                                   Text="@convertToPart" /> | ||||
|                 } else { | ||||
|                     <MudSelect T="string" | ||||
|                                Label="Affected Customer" | ||||
|                                Required | ||||
|                                Variant="Variant.Outlined" | ||||
|                                AnchorOrigin="Origin.BottomCenter" | ||||
|                                @bind-Value=mrbAction.Customer | ||||
|                                Text="@mrbAction.Customer"> | ||||
|                         @foreach (string customer in customerNames) { | ||||
|                             <MudSelectItem Value="@(customer)" /> | ||||
|                         } | ||||
|                     </MudSelect> | ||||
|                     <MudAutocomplete T="string" | ||||
|                                      Label="Part Number" | ||||
|                                      Variant="Variant.Outlined" | ||||
|                                      AnchorOrigin="Origin.BottomCenter" | ||||
|                                      @bind-Value=mrbAction.PartNumber | ||||
|                                      Text="@mrbAction.PartNumber" | ||||
|                                      SearchFunc="@PartNumberSearch" | ||||
|                                      ResetValueOnEmptyText | ||||
|                                      CoerceText /> | ||||
|                 } | ||||
|                 <MudTextField T="int" | ||||
|                               InputType="@InputType.Number" | ||||
|                               Label="Qty" | ||||
|                               Required="true" | ||||
|                               RequiredError="You must supply a quantity!" | ||||
|                               @bind-Value=mrbAction.Quantity | ||||
|                               Text="@mrbAction.Quantity.ToString()" /> | ||||
|                 <MudTextField T="string" | ||||
|                               Label="Part Number" | ||||
|                               Required="true" | ||||
|                               RequiredError="Part number required!" | ||||
|                               @bind-Value="@mrbAction.PartNumber" | ||||
|                               Text="@mrbAction.PartNumber" /> | ||||
|                 <MudTextField T="string" | ||||
|                               Label="Batch Number / Lot Number" | ||||
|                               Required="true" | ||||
|                               RequiredError="Batch number / Lot number required!" | ||||
|                               @bind-Value="@mrbAction.LotNumber" | ||||
|                               Text="@mrbAction.LotNumber" /> | ||||
|                 <MudAutocomplete T="string" | ||||
|                                  Label="Batch Number / Lot Number" | ||||
|                                  Variant="Variant.Outlined" | ||||
|                                  AnchorOrigin="Origin.BottomCenter" | ||||
|                                  @bind-Value=mrbAction.LotNumber | ||||
|                                  Text="@mrbAction.LotNumber" | ||||
|                                  SearchFunc="@LotNumberSearch" | ||||
|                                  ResetValueOnEmptyText | ||||
|                                  CoerceText /> | ||||
|                 @if (mrbAction.Action.Equals("Scrap")) { | ||||
|                     <MudSelect T="string" | ||||
|                                Label="Justification" | ||||
|                                Required | ||||
|                                RequiredError="Justification required!" | ||||
|                                Variant="Variant.Outlined" | ||||
|                                AnchorOrigin="Origin.BottomCenter" | ||||
|                                @bind-Value=mrbAction.Justification | ||||
|                                Text="@mrbAction.Justification"> | ||||
|                         <MudSelectItem Value="@("Obsolete")" /> | ||||
|                         <MudSelectItem Value="@("Aged Material")" /> | ||||
|                         <MudSelectItem Value="@("Out of Specification")" /> | ||||
|                     </MudSelect> | ||||
|                 } | ||||
|             </MudForm> | ||||
|         </MudPaper> | ||||
|     </DialogContent> | ||||
| @ -86,6 +148,17 @@ | ||||
|     [Parameter] | ||||
|     public MRBAction mrbAction { get; set; } | ||||
|  | ||||
|     private IEnumerable<MRBAction>? actions = null; | ||||
|  | ||||
|     private MRBAction lastAction = null; | ||||
|  | ||||
|     private IEnumerable<string> customerNames = new List<string>(); | ||||
|  | ||||
|     private string convertFromCustomer = string.Empty; | ||||
|     private string convertFromPart = string.Empty; | ||||
|     private string convertToCustomer = string.Empty; | ||||
|     private string convertToPart = string.Empty; | ||||
|  | ||||
|     private bool isVisible { get; set; } | ||||
|     private string[] errors = { }; | ||||
|     private bool processingSave = false; | ||||
| @ -95,16 +168,46 @@ | ||||
|         isVisible = true; | ||||
|  | ||||
|         if (mrbAction is null) { | ||||
|             mrbAction = new() { | ||||
|                     Action = "", | ||||
|                     Customer = "", | ||||
|                     Quantity = 0, | ||||
|                     PartNumber = "", | ||||
|                     LotNumber = "", | ||||
|                     MRBNumber = 0 | ||||
|                 }; | ||||
|             snackbar.Add("MRB action cannot be null", Severity.Warning); | ||||
|             MudDialog.Cancel(); | ||||
|         } else { | ||||
|             actions = (await mrbService.GetMRBActionsForMRB(mrbAction.MRBNumber, false)).OrderByDescending(a => a.ActionID); | ||||
|  | ||||
|             if (actions is not null && actions.Count() > 0) { | ||||
|                 if (string.IsNullOrWhiteSpace(mrbAction.Action)) | ||||
|                     mrbAction.Action = actions.First().Action; | ||||
|                 if (string.IsNullOrWhiteSpace(mrbAction.Customer)) | ||||
|                     mrbAction.Customer = actions.First().Customer; | ||||
|                 if (string.IsNullOrWhiteSpace(mrbAction.LotNumber)) | ||||
|                     mrbAction.LotNumber = actions.First().LotNumber; | ||||
|                 if (string.IsNullOrWhiteSpace(mrbAction.PartNumber)) | ||||
|                     mrbAction.PartNumber = actions.First().PartNumber; | ||||
|                 if (string.IsNullOrWhiteSpace(mrbAction.Justification)) | ||||
|                     mrbAction.Justification = actions.First().Justification; | ||||
|                 if (mrbAction.Quantity == 0) | ||||
|                     mrbAction.Quantity = actions.First().Quantity; | ||||
|  | ||||
|                 if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) { | ||||
|                     string[] convertFrom = actions.First().ConvertFrom.Split(" "); | ||||
|                     if (convertFrom.Length > 1) { | ||||
|                         convertFromCustomer = convertFrom[0]; | ||||
|                         foreach (string partStr in convertFrom.Skip(1)) | ||||
|                             convertFromPart += partStr; | ||||
|                     } | ||||
|  | ||||
|                     string[] convertTo = actions.First().ConvertTo.Split(" "); | ||||
|                     if (convertTo.Length > 1) { | ||||
|                         convertToCustomer = convertTo[0]; | ||||
|                         foreach (string partStr in convertTo.Skip(1)) | ||||
|                             convertToPart += partStr; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (customerNames is null || customerNames.Count() <= 0) | ||||
|             customerNames = await customerService.GetAllCustomerNames(); | ||||
|  | ||||
|         StateHasChanged(); | ||||
|     } | ||||
|  | ||||
| @ -112,9 +215,22 @@ | ||||
|         bool actionIsValid = mrbAction.Action.Equals("Block") || mrbAction.Action.Equals("Convert") || | ||||
|             mrbAction.Action.Equals("Other") || mrbAction.Action.Equals("Recall") || mrbAction.Action.Equals("Scrap") || | ||||
|             mrbAction.Action.Equals("Unblock") || mrbAction.Action.Equals("Waiver"); | ||||
|         return actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Customer) && | ||||
|         actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Customer) && | ||||
|             !string.IsNullOrWhiteSpace(mrbAction.PartNumber) && | ||||
|             !string.IsNullOrWhiteSpace(mrbAction.LotNumber); | ||||
|         actionIsValid = actionIsValid && mrbAction.Quantity > 0; | ||||
|  | ||||
|         if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) { | ||||
|             actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(convertFromCustomer) && | ||||
|                 !string.IsNullOrWhiteSpace(convertFromPart) && | ||||
|                 !string.IsNullOrWhiteSpace(convertToCustomer) && | ||||
|                 !string.IsNullOrWhiteSpace(convertToPart); | ||||
|         } | ||||
|  | ||||
|         if (mrbAction.Action.Equals("Scrap", StringComparison.InvariantCultureIgnoreCase)) | ||||
|             actionIsValid = actionIsValid && !string.IsNullOrWhiteSpace(mrbAction.Justification); | ||||
|  | ||||
|         return actionIsValid; | ||||
|     } | ||||
|  | ||||
|     private async void SaveMRBAction() { | ||||
| @ -122,6 +238,11 @@ | ||||
|         try { | ||||
|             if (!FormIsValid()) throw new Exception("You must complete the form before saving!"); | ||||
|  | ||||
|             if (mrbAction.Action.Equals("Convert", StringComparison.InvariantCultureIgnoreCase)) { | ||||
|                 mrbAction.ConvertFrom = $"{convertFromCustomer} {convertFromPart}"; | ||||
|                 mrbAction.ConvertTo = $"{convertToCustomer} {convertToPart}"; | ||||
|             } | ||||
|  | ||||
|             if (mrbAction.MRBNumber > 0) { | ||||
|                 if (mrbAction.ActionID <= 0) { | ||||
|                     await mrbService.CreateMRBAction(mrbAction); | ||||
| @ -130,10 +251,14 @@ | ||||
|                     await mrbService.UpdateMRBAction(mrbAction); | ||||
|                     snackbar.Add("MRB action updated", Severity.Success); | ||||
|                 } | ||||
|  | ||||
|                 actions = (await mrbService.GetMRBActionsForMRB(mrbAction.MRBNumber, true)).OrderByDescending(a => a.ActionID); | ||||
|             } else { | ||||
|                 snackbar.Add("MRB action saved", Severity.Success); | ||||
|             } | ||||
|  | ||||
|             StateHasChanged(); | ||||
|  | ||||
|             MudDialog.Close(DialogResult.Ok(mrbAction)); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
| @ -153,6 +278,8 @@ | ||||
|             await mrbService.DeleteMRBAction(mrbAction); | ||||
|             snackbar.Add("MRB action successfully deleted", Severity.Success); | ||||
|  | ||||
|             StateHasChanged(); | ||||
|  | ||||
|             MudDialog.Close(DialogResult.Ok<MRBAction>(null)); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
| @ -163,4 +290,38 @@ | ||||
|     private void Cancel() { | ||||
|         MudDialog.Cancel(); | ||||
|     } | ||||
|  | ||||
|     private async Task<IEnumerable<string>> PartNumberSearch(string value, CancellationToken token) { | ||||
|         if (actions is null) return new List<string> { value }; | ||||
|  | ||||
|         if (string.IsNullOrWhiteSpace(value)) return new string[0]; | ||||
|  | ||||
|         HashSet<string> partNumbers = new(); | ||||
|  | ||||
|         partNumbers.Add(value); | ||||
|  | ||||
|         foreach (MRBAction action in actions) { | ||||
|             if (action.PartNumber.Contains(value, StringComparison.InvariantCultureIgnoreCase)) | ||||
|                 partNumbers.Add(action.PartNumber); | ||||
|         } | ||||
|  | ||||
|         return partNumbers; | ||||
|     } | ||||
|  | ||||
|     private async Task<IEnumerable<string>> LotNumberSearch(string value, CancellationToken token) { | ||||
|         if (actions is null) return new List<string> { value }; | ||||
|  | ||||
|         if (string.IsNullOrWhiteSpace(value)) return new string[0]; | ||||
|  | ||||
|         HashSet<string> lotNumbers = new(); | ||||
|  | ||||
|         lotNumbers.Add(value); | ||||
|  | ||||
|         foreach (MRBAction action in actions) { | ||||
|             if (action.LotNumber.Contains(value, StringComparison.InvariantCultureIgnoreCase)) | ||||
|                 lotNumbers.Add(action.LotNumber); | ||||
|         } | ||||
|  | ||||
|         return lotNumbers; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,111 @@ | ||||
| @inject IApprovalService approvalService | ||||
| @inject ISnackbar snackbar | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         @if (availableApprovers is not null) { | ||||
|             <MudPaper Class="m-2 p-2"> | ||||
|                 <MudSelect T="User" | ||||
|                            Label="Select a User" | ||||
|                            Required | ||||
|                            Variant="Variant.Outlined" | ||||
|                            AnchorOrigin="Origin.BottomCenter" | ||||
|                            @bind-Value=selectedUser | ||||
|                            Text="@(selectedUser is null ? "" : selectedUser.GetFullName())"> | ||||
|                     @foreach (User user in availableApprovers) { | ||||
|                         <MudSelectItem Value="@user"> | ||||
|                             @user.GetFullName() | ||||
|                         </MudSelectItem> | ||||
|                     } | ||||
|                 </MudSelect> | ||||
|             </MudPaper> | ||||
|         } | ||||
|     </DialogContent> | ||||
|     <DialogActions> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Color="Color.Tertiary" | ||||
|                    Class="m1-auto" | ||||
|                    OnClick=Submit> | ||||
|             <MudText>Submit</MudText> | ||||
|         </MudButton> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Color="Color.Secondary" | ||||
|                    Class="m1-auto" | ||||
|                    OnClick=Cancel> | ||||
|             <MudText>Cancel</MudText> | ||||
|         </MudButton> | ||||
|     </DialogActions> | ||||
| </MudDialog> | ||||
|  | ||||
| <MudOverlay Visible=processing DarkBackground="true" AutoClose="false"> | ||||
|     <MudProgressCircular Color="Color.Info" Size="Size.Medium" Indeterminate="true" /> | ||||
| </MudOverlay> | ||||
|  | ||||
| @code { | ||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } | ||||
|  | ||||
|     [Parameter] | ||||
|     public User selectedUser { get; set; } | ||||
|  | ||||
|     private bool processing = false; | ||||
|  | ||||
|     private HashSet<User> availableApprovers = new(); | ||||
|  | ||||
|     protected override async Task OnInitializedAsync() { | ||||
|         try { | ||||
|             processing = true; | ||||
|  | ||||
|             string roleName = "QA_PRE_APPROVAL"; | ||||
|             string subRoleName = "QA_PRE_APPROVAL"; | ||||
|  | ||||
|             IEnumerable<User> qaApprovers = await GetApprovalGroupMembersForRoleAndSubRole(roleName, subRoleName); | ||||
|  | ||||
|             foreach (User approver in qaApprovers) | ||||
|                 availableApprovers.Add(approver); | ||||
|  | ||||
|             roleName = "MRB Approver"; | ||||
|             subRoleName = "MRBApprover"; | ||||
|  | ||||
|             IEnumerable<User> mrbApprovers = await GetApprovalGroupMembersForRoleAndSubRole(roleName, subRoleName); | ||||
|  | ||||
|             foreach (User approver in mrbApprovers) | ||||
|                 availableApprovers.Add(approver); | ||||
|  | ||||
|             selectedUser = availableApprovers.First(); | ||||
|  | ||||
|             processing = false; | ||||
|         } catch (Exception ex) { | ||||
|             processing = false; | ||||
|             snackbar.Add($"Unable to get all approvers, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void Submit() { | ||||
|         MudDialog.Close(DialogResult.Ok(selectedUser)); | ||||
|     } | ||||
|  | ||||
|     private void Cancel() { | ||||
|         MudDialog.Close(DialogResult.Cancel()); | ||||
|     } | ||||
|  | ||||
|     private async Task<IEnumerable<User>> GetApprovalGroupMembersForRoleAndSubRole(string roleName, string subRoleName) { | ||||
|         HashSet<User> members = new(); | ||||
|  | ||||
|         int roleId = await approvalService.GetRoleIdForRoleName(roleName); | ||||
|  | ||||
|         if (roleId <= 0) throw new Exception($"could not find {roleName} role ID"); | ||||
|  | ||||
|         IEnumerable<SubRole> subRoles = await approvalService.GetSubRolesForSubRoleName(subRoleName, roleId); | ||||
|  | ||||
|         foreach (SubRole subRole in subRoles) { | ||||
|             IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); | ||||
|  | ||||
|             foreach (User member in subRoleMembers) { | ||||
|                 members.Add(member); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return members; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										151
									
								
								MesaFabApproval.Client/Pages/Components/PCR3DocumentForm.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								MesaFabApproval.Client/Pages/Components/PCR3DocumentForm.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
| @inject NavigationManager navigationManager | ||||
| @inject IPCRBService pcrbService | ||||
| @inject IUserService userService | ||||
| @inject IECNService ecnService | ||||
| @inject ISnackbar snackbar | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         <MudPaper Class="m-2 p-2"> | ||||
|             <MudForm @bind-Errors="@errors"> | ||||
|                 <MudTextField T="string" | ||||
|                               Label="Document Type" | ||||
|                               @bind-Value="@document.DocType" | ||||
|                               @bind-Text="@document.DocType" | ||||
|                               Disabled | ||||
|                               AutoGrow /> | ||||
|                 <MudTextField Label="Document Numbers - Rev. & Title" | ||||
|                               @bind-Value="@document.DocNumbers" | ||||
|                               @bind-Text="@document.DocNumbers" | ||||
|                               Immediate | ||||
|                               AutoGrow | ||||
|                               AutoFocus /> | ||||
|                 @if (DocNumberIsNA()) { | ||||
|                     <MudTextField Label="Comments" | ||||
|                                   @bind-Value="@document.Comment" | ||||
|                                   @bind-Text="@document.Comment" | ||||
|                                   Required | ||||
|                                   RequiredError="You must provide a comment" | ||||
|                                   Immediate | ||||
|                                   AutoGrow /> | ||||
|                 } else { | ||||
|                     <MudTextField @bind-Value="@document.ECNNumber" | ||||
|                                   Required | ||||
|                                   RequiredError="You must provide a valid ECN#" | ||||
|                                   Clearable | ||||
|                                   Variant="Variant.Outlined" | ||||
|                                   InputType="@InputType.Number" | ||||
|                                   Validation="@(new Func<int, Task<string>>(ECNNoIsValid))" | ||||
|                                   Label="ECN#" | ||||
|                                   Immediate | ||||
|                                   AutoGrow /> | ||||
|                 } | ||||
|                 <MudCheckBox Label="Complete" | ||||
|                              Color="Color.Tertiary" | ||||
|                              @bind-Value=complete | ||||
|                              LabelPosition="LabelPosition.Start" /> | ||||
|             </MudForm> | ||||
|         </MudPaper> | ||||
|     </DialogContent> | ||||
|     <DialogActions> | ||||
|         @if ((DocNumberIsNA() && !string.IsNullOrWhiteSpace(document.Comment)) ||  | ||||
|              (!DocNumberIsNA() && ecnNoIsValid)) { | ||||
|             <MudButton Variant="Variant.Filled" | ||||
|                        Color="Color.Tertiary" | ||||
|                        Class="m1-auto" | ||||
|                        OnClick=Save> | ||||
|                 @if (saveInProcess) { | ||||
|                     <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                     <MudText>Processing</MudText> | ||||
|                 } else { | ||||
|                     <MudText>Save</MudText> | ||||
|                 } | ||||
|             </MudButton> | ||||
|         } | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Class="grey text-black m1-auto" | ||||
|                    OnClick=Cancel> | ||||
|             Cancel | ||||
|         </MudButton> | ||||
|     </DialogActions> | ||||
| </MudDialog> | ||||
|  | ||||
| @code { | ||||
|     [CascadingParameter]  | ||||
|     MudDialogInstance MudDialog { get; set; } | ||||
|  | ||||
|     [Parameter] | ||||
|     public required PCR3Document document { get; set; } | ||||
|  | ||||
|     private string[] errors = { }; | ||||
|  | ||||
|     private bool complete = false; | ||||
|  | ||||
|     private bool saveInProcess = false; | ||||
|  | ||||
|     private bool ecnNoIsValid = true; | ||||
|  | ||||
|     protected override async Task OnParametersSetAsync() { | ||||
|         complete = document.CompletedByID > 0; | ||||
|     } | ||||
|  | ||||
|     private async Task Save() { | ||||
|         saveInProcess = true; | ||||
|         try { | ||||
|             if (authStateProvider.CurrentUser is null) { | ||||
|                 await authStateProvider.Logout(); | ||||
|                 navigationManager.NavigateTo("login"); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (!complete) { | ||||
|                 document.CompletedByID = 0; | ||||
|                 document.CompletedBy = null; | ||||
|                 document.CompletedDate = DateTimeUtilities.MAX_DT; | ||||
|             } | ||||
|  | ||||
|             if (complete && document.CompletedByID <= 0) { | ||||
|                 document.CompletedByID = authStateProvider.CurrentUser.UserID; | ||||
|                 document.CompletedBy = authStateProvider.CurrentUser; | ||||
|                 document.CompletedDate = DateTime.Now; | ||||
|             } | ||||
|  | ||||
|             if (!DocNumberIsNA() && !ecnNoIsValid)  | ||||
|                 throw new Exception($"{document.ECNNumber} is not a valid ECN#"); | ||||
|             if (DocNumberIsNA() && string.IsNullOrWhiteSpace(document.Comment)) | ||||
|                 throw new Exception("you must provide a comment"); | ||||
|  | ||||
|             await pcrbService.UpdatePCR3Document(document); | ||||
|  | ||||
|             await pcrbService.GetPCR3DocumentsForPlanNumber(document.PlanNumber, true); | ||||
|  | ||||
|             saveInProcess = false; | ||||
|  | ||||
|             MudDialog.Close(DialogResult.Ok(document)); | ||||
|         } catch (Exception ex) { | ||||
|             saveInProcess = false; | ||||
|             snackbar.Add($"Unable to save document, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void Cancel() { | ||||
|         MudDialog.Close(DialogResult.Cancel()); | ||||
|     } | ||||
|  | ||||
|     private bool DocNumberIsNA() { | ||||
|         if (document.DocNumbers.ToLower().Equals("na") || | ||||
|             document.DocNumbers.ToLower().Equals("n/a")) { | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private async Task<string> ECNNoIsValid(int ecnNumber) { | ||||
|         string? result = await ecnService.ECNNumberIsValidStr(ecnNumber); | ||||
|         if (result is null) ecnNoIsValid = true; | ||||
|         else ecnNoIsValid = false; | ||||
|         StateHasChanged(); | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										201
									
								
								MesaFabApproval.Client/Pages/Components/PCRBActionItemForm.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								MesaFabApproval.Client/Pages/Components/PCRBActionItemForm.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,201 @@ | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
| @inject NavigationManager navigationManager | ||||
| @inject IPCRBService pcrbService | ||||
| @inject IUserService userService | ||||
| @inject ISnackbar snackbar | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         <MudPaper Class="m-2 p-2"> | ||||
|             <MudForm @bind-Errors="@errors"> | ||||
|                 <MudTextField T="string" | ||||
|                               Label="Action" | ||||
|                               Required | ||||
|                               RequiredError="Enter action item" | ||||
|                               @bind-Value="@name" | ||||
|                               @bind-Text="@name" | ||||
|                               Immediate | ||||
|                               Clearable | ||||
|                               AutoGrow | ||||
|                               AutoFocus /> | ||||
|                 <MudCheckBox Label="Gating" | ||||
|                              Color="Color.Tertiary" | ||||
|                              @bind-Value=gating | ||||
|                              LabelPosition="LabelPosition.Start" /> | ||||
|                 <MudCheckBox Label="Closed" | ||||
|                              Color="Color.Tertiary" | ||||
|                              @bind-Value="@closedStatus" | ||||
|                              LabelPosition="LabelPosition.Start" /> | ||||
|                 @if (closedStatus) { | ||||
|                     <MudDatePicker Label="Closed Date" | ||||
|                                    Color="Color.Tertiary" | ||||
|                                    @bind-Date="@closedDate" | ||||
|                                    Clearable | ||||
|                                    MinDate="@DateTimeUtilities.MIN_DT" | ||||
|                                    MaxDate="@DateTimeUtilities.MAX_DT" | ||||
|                                    Placeholder="Select a closed date" /> | ||||
|                 } | ||||
|                 <MudSelect T="User" | ||||
|                            Label="Responsible Person" | ||||
|                            Variant="Variant.Outlined" | ||||
|                            Required | ||||
|                            RequiredError="You must select a responsible person" | ||||
|                            Clearable | ||||
|                            AnchorOrigin="Origin.BottomCenter" | ||||
|                            ToStringFunc="@UserToNameConverter" | ||||
|                            @bind-Value=@responsiblePerson> | ||||
|                     @foreach (User user in allActiveUsers.OrderBy(u => u.FirstName)) { | ||||
|                         <MudSelectItem T="User" Value="@(user)" /> | ||||
|                     } | ||||
|                 </MudSelect> | ||||
|             </MudForm> | ||||
|         </MudPaper> | ||||
|     </DialogContent> | ||||
|     <DialogActions> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Color="Color.Tertiary" | ||||
|                    Class="m1-auto" | ||||
|                    OnClick=Save> | ||||
|             @if (saveActionItemInProcess) { | ||||
|                 <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                 <MudText>Processing</MudText> | ||||
|             } else { | ||||
|                 <MudText>Submit</MudText> | ||||
|             } | ||||
|         </MudButton> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Class="grey text-black m1-auto" | ||||
|                    OnClick=Cancel> | ||||
|             Cancel | ||||
|         </MudButton> | ||||
|     </DialogActions> | ||||
| </MudDialog> | ||||
|  | ||||
| @code { | ||||
|     [CascadingParameter]  | ||||
|     MudDialogInstance MudDialog { get; set; } | ||||
|  | ||||
|     [Parameter] | ||||
|     public int planNumber { get; set; } = 0; | ||||
|  | ||||
|     [Parameter] | ||||
|     public int step { get; set; } = 0; | ||||
|  | ||||
|     [Parameter] | ||||
|     public PCRBActionItem? actionItem { get; set; } = null; | ||||
|  | ||||
|     [Parameter] | ||||
|     public IEnumerable<User> allActiveUsers { get; set; } | ||||
|  | ||||
|     private string[] errors = { }; | ||||
|  | ||||
|     private string name = ""; | ||||
|     private bool gating = false; | ||||
|     private DateTime? closedDate = DateTimeUtilities.MAX_DT; | ||||
|     private bool closedStatus = false; | ||||
|     private User? responsiblePerson = null; | ||||
|  | ||||
|     private bool saveActionItemInProcess = false; | ||||
|  | ||||
|     protected override async Task OnParametersSetAsync() { | ||||
|         if (authStateProvider.CurrentUser is null) { | ||||
|             await authStateProvider.Logout(); | ||||
|             navigationManager.NavigateTo("login"); | ||||
|         } | ||||
|  | ||||
|         if (planNumber <= 0) { | ||||
|             snackbar.Add($"{planNumber} is not a valid PCRB plan#", Severity.Error); | ||||
|             MudDialog.Close(DialogResult.Cancel()); | ||||
|         } | ||||
|  | ||||
|         if (allActiveUsers is null || allActiveUsers.Count() <= 0) | ||||
|             allActiveUsers = await userService.GetAllActiveUsers(); | ||||
|  | ||||
|         if (actionItem is not null) { | ||||
|             name = actionItem.Name; | ||||
|             gating = actionItem.Gating; | ||||
|             closedStatus = actionItem.ClosedStatus; | ||||
|             closedDate = actionItem.ClosedDate; | ||||
|             if (closedDate.Equals(DateTimeUtilities.MAX_DT)) { | ||||
|                 closedDate = DateTime.Now; | ||||
|             } | ||||
|             if (actionItem.ResponsiblePersonID > 0) { | ||||
|                 if (actionItem.ResponsiblePerson is not null) responsiblePerson = actionItem.ResponsiblePerson; | ||||
|                 else responsiblePerson = await userService.GetUserByUserId(actionItem.ResponsiblePersonID); | ||||
|                 actionItem.ResponsiblePerson = responsiblePerson; | ||||
|             } | ||||
|         } else { | ||||
|             name = ""; | ||||
|             gating = false; | ||||
|             closedStatus = false; | ||||
|             closedDate = DateTime.Now; | ||||
|             responsiblePerson = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task Save() { | ||||
|         saveActionItemInProcess = true; | ||||
|         try { | ||||
|             if (authStateProvider.CurrentUser is null) { | ||||
|                 await authStateProvider.Logout(); | ||||
|                 navigationManager.NavigateTo("login"); | ||||
|             } | ||||
|  | ||||
|             if (string.IsNullOrWhiteSpace(name)) throw new Exception("name missing"); | ||||
|  | ||||
|             if (actionItem is null) { | ||||
|                 actionItem = new() { | ||||
|                     Name = name, | ||||
|                     UploadedBy = authStateProvider.CurrentUser, | ||||
|                     UploadedByID = authStateProvider.CurrentUser.UserID, | ||||
|                     PlanNumber = planNumber, | ||||
|                     Step = step, | ||||
|                     Gating = gating, | ||||
|                     ClosedStatus = closedStatus, | ||||
|                     ResponsiblePerson = responsiblePerson, | ||||
|                     ResponsiblePersonID = responsiblePerson.UserID, | ||||
|                     ClosedDate = closedDate, | ||||
|                     ClosedBy = closedDate is null || closedDate >= DateTimeUtilities.MAX_DT ? null : authStateProvider.CurrentUser, | ||||
|                     ClosedByID = closedStatus ? authStateProvider.CurrentUser.UserID : 0 | ||||
|                 }; | ||||
|  | ||||
|                 if (actionItem.ClosedStatus == false) { | ||||
|                     actionItem.ClosedDate = DateTimeUtilities.MAX_DT; | ||||
|                 } | ||||
|  | ||||
|                 await pcrbService.CreateNewActionItem(actionItem); | ||||
|             } else { | ||||
|                 actionItem.Name = name; | ||||
|                 actionItem.Gating = gating; | ||||
|                 actionItem.ClosedStatus = closedStatus; | ||||
|                 actionItem.ClosedDate = closedDate; | ||||
|                 if (closedStatus) { | ||||
|                     actionItem.ClosedBy = authStateProvider.CurrentUser; | ||||
|                     actionItem.ClosedByID = authStateProvider.CurrentUser.UserID; | ||||
|                 } else { | ||||
|                     actionItem.ClosedDate = DateTimeUtilities.MAX_DT; | ||||
|                 } | ||||
|                 actionItem.ResponsiblePerson = responsiblePerson; | ||||
|                 if (responsiblePerson is not null) | ||||
|                     actionItem.ResponsiblePersonID = responsiblePerson.UserID; | ||||
|  | ||||
|                 await pcrbService.UpdateActionItem(actionItem); | ||||
|             } | ||||
|  | ||||
|             saveActionItemInProcess = false; | ||||
|  | ||||
|             MudDialog.Close(DialogResult.Ok(actionItem)); | ||||
|         } catch (Exception ex) { | ||||
|             saveActionItemInProcess = false; | ||||
|             snackbar.Add($"Unable to save action item, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void Cancel() { | ||||
|         MudDialog.Close(DialogResult.Cancel()); | ||||
|     } | ||||
|  | ||||
|     private Func<User, string> UserToNameConverter = u => u is null ? string.Empty : u.GetFullName(); | ||||
|  | ||||
|     private Func<PCRBAttachment, string> AttachmentToFileNameConverter = a => a is null ? "" : a.FileName; | ||||
| } | ||||
							
								
								
									
										250
									
								
								MesaFabApproval.Client/Pages/Components/PCRBApproverForm.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								MesaFabApproval.Client/Pages/Components/PCRBApproverForm.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,250 @@ | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
| @inject NavigationManager navigationManager | ||||
| @inject IPCRBService pcrbService | ||||
| @inject IUserService userService | ||||
| @inject IApprovalService approvalService | ||||
| @inject ISnackbar snackbar | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         <MudPaper Class="m-2 p-2"> | ||||
|             <MudForm @bind-Errors="@errors"> | ||||
|                 <MudSelect T="string" | ||||
|                               Label="Job Title" | ||||
|                               Required | ||||
|                               RequiredError="You must provide a job title" | ||||
|                               ValueChanged="@SelectedJobTitleChanged" | ||||
|                               @bind-Text="@selectedJobTitle" | ||||
|                               Immediate | ||||
|                               Disabled="@(approval is not null && !string.IsNullOrWhiteSpace(selectedJobTitle))" | ||||
|                               Clearable | ||||
|                               AutoFocus > | ||||
|                     @foreach (string jt in availableJobTitles) { | ||||
|                         <MudSelectItem Value="@jt">@jt</MudSelectItem> | ||||
|                     } | ||||
|                 </MudSelect> | ||||
|                 <MudSelect T="User" | ||||
|                            Label="Select a User" | ||||
|                            Required | ||||
|                            Clearable | ||||
|                            Disabled="@(string.IsNullOrWhiteSpace(selectedJobTitle))" | ||||
|                            Variant="Variant.Outlined" | ||||
|                            AnchorOrigin="Origin.BottomCenter" | ||||
|                            @bind-Value=selectedUser | ||||
|                            ToStringFunc="@UserToNameConverter"> | ||||
|                     @foreach (User user in availableUsers) { | ||||
|                         <MudSelectItem Value="@user">@user.GetFullName()</MudSelectItem> | ||||
|                     } | ||||
|                 </MudSelect> | ||||
|             </MudForm> | ||||
|         </MudPaper> | ||||
|     </DialogContent> | ||||
|     <DialogActions> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Color="Color.Tertiary" | ||||
|                    Class="m1-auto" | ||||
|                    OnClick=Save> | ||||
|             @if (saveAttendeeInProcess) { | ||||
|                 <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                 <MudText>Processing</MudText> | ||||
|             } else { | ||||
|                 <MudText>Submit</MudText> | ||||
|             } | ||||
|         </MudButton> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Class="grey text-black m1-auto" | ||||
|                    OnClick=Cancel> | ||||
|             Cancel | ||||
|         </MudButton> | ||||
|     </DialogActions> | ||||
| </MudDialog> | ||||
|  | ||||
| @code { | ||||
|     [CascadingParameter]  | ||||
|     MudDialogInstance MudDialog { get; set; } | ||||
|  | ||||
|     [Parameter] | ||||
|     public int planNumber { get; set; } = 0; | ||||
|  | ||||
|     [Parameter] | ||||
|     public int step { get; set; } = 0; | ||||
|  | ||||
|     [Parameter] | ||||
|     public Approval? approval { get; set; } = null; | ||||
|  | ||||
|     private HashSet<string> availableJobTitles = new(); | ||||
|  | ||||
|     private Dictionary<string, SubRole> jobTitleToSubRoleMap = new(); | ||||
|  | ||||
|     private Dictionary<string, IEnumerable<User>> jobTitleToAvailableUsersMap = new(); | ||||
|  | ||||
|     private HashSet<User> availableUsers = new(); | ||||
|  | ||||
|     private string[] errors = { }; | ||||
|  | ||||
|     private string selectedJobTitle = ""; | ||||
|     private User? selectedUser = null; | ||||
|  | ||||
|     private bool saveAttendeeInProcess = false; | ||||
|  | ||||
|     protected override async Task OnParametersSetAsync() { | ||||
|         if (authStateProvider.CurrentUser is null) { | ||||
|             await authStateProvider.Logout(); | ||||
|             navigationManager.NavigateTo("login"); | ||||
|         } | ||||
|  | ||||
|         if (planNumber <= 0) { | ||||
|             snackbar.Add($"{planNumber} is not a valid PCRB plan#", Severity.Error); | ||||
|             MudDialog.Close(DialogResult.Cancel()); | ||||
|         } | ||||
|  | ||||
|         await GetAttendees(); | ||||
|  | ||||
|         if (approval is not null) { | ||||
|             selectedJobTitle = approval.RoleName; | ||||
|             if (approval.UserID > 0) { | ||||
|                 if (approval.User is not null) { | ||||
|                     selectedUser = approval.User; | ||||
|                 } else { | ||||
|                     selectedUser = await userService.GetUserByUserId(approval.UserID); | ||||
|                     approval.User = selectedUser; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             SelectedJobTitleChanged(selectedJobTitle); | ||||
|         } else { | ||||
|             selectedJobTitle = ""; | ||||
|             selectedUser = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task Save() { | ||||
|         saveAttendeeInProcess = true; | ||||
|         try { | ||||
|             if (authStateProvider.CurrentUser is null) { | ||||
|                 await authStateProvider.Logout(); | ||||
|                 navigationManager.NavigateTo("login"); | ||||
|             } | ||||
|  | ||||
|             if (string.IsNullOrWhiteSpace(selectedJobTitle)) throw new Exception("job title missing"); | ||||
|             if (selectedUser is null) throw new Exception("attendee not selected"); | ||||
|  | ||||
|             PCRB pcrb = await pcrbService.GetPCRBByPlanNumber(planNumber, false); | ||||
|  | ||||
|             if (approval is null) { | ||||
|                 jobTitleToSubRoleMap.TryGetValue($"{selectedJobTitle}{selectedUser.UserID}", out SubRole? subRole); | ||||
|  | ||||
|                 if (subRole is null) throw new Exception($"no approval role found for job title {selectedJobTitle}"); | ||||
|  | ||||
|                 approval = new() { | ||||
|                     RoleName = subRole.SubRoleCategoryItem, | ||||
|                     SubRole = subRole.SubRoleName, | ||||
|                     SubRoleID = subRole.SubRoleID, | ||||
|                     IssueID = planNumber, | ||||
|                     Step = step, | ||||
|                     User = selectedUser, | ||||
|                     UserID = selectedUser.UserID, | ||||
|                     AssignedDate = DateTimeUtilities.MIN_DT | ||||
|                 }; | ||||
|  | ||||
|                 await approvalService.CreateApproval(approval); | ||||
|             } else { | ||||
|                 int originalUserId = approval.UserID; | ||||
|  | ||||
|                 approval.UserID = selectedUser.UserID; | ||||
|                 approval.User = selectedUser; | ||||
|  | ||||
|                 if (originalUserId != approval.UserID) { | ||||
|                     if (approval.AssignedDate > DateTimeUtilities.MIN_DT) | ||||
|                         approval.AssignedDate = DateTime.Now; | ||||
|                     approval.NotifyDate = DateTimeUtilities.MIN_DT; | ||||
|                 } | ||||
|  | ||||
|                 await approvalService.UpdateApproval(approval); | ||||
|  | ||||
|                 if (originalUserId != approval.UserID) | ||||
|                     await pcrbService.NotifyNewApprovals(pcrb); | ||||
|             } | ||||
|  | ||||
|             saveAttendeeInProcess = false; | ||||
|  | ||||
|             MudDialog.Close(DialogResult.Ok(approval)); | ||||
|         } catch (Exception ex) { | ||||
|             saveAttendeeInProcess = false; | ||||
|             snackbar.Add($"Unable to save attendee, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void Cancel() { | ||||
|         MudDialog.Close(DialogResult.Cancel()); | ||||
|     } | ||||
|  | ||||
|     private Func<User, string> UserToNameConverter = u => u is null ? string.Empty : u.GetFullName(); | ||||
|  | ||||
|     private async Task GetAttendees() { | ||||
|         int roleId = await approvalService.GetRoleIdForRoleName("Module Manager"); | ||||
|  | ||||
|         if (roleId <= 0) throw new Exception($"could not find Director role ID"); | ||||
|  | ||||
|         availableJobTitles.Clear(); | ||||
|         jobTitleToAvailableUsersMap.Clear(); | ||||
|         jobTitleToSubRoleMap.Clear(); | ||||
|  | ||||
|         IEnumerable<SubRole> subRoles = await approvalService.GetSubRolesForSubRoleName("MMSubRole", roleId); | ||||
|  | ||||
|         HashSet<string> defaultSubRoleCategoryItems = new() { "Si Production", "Si Engineering", "Quality" }; | ||||
|         HashSet<string> unusedSubRoleCategoryItems = new() { "GaN Engineering", "GaN Operations", "Integration" }; | ||||
|         foreach (SubRole subRole in subRoles) { | ||||
|             if (approval is null) { | ||||
|                 if (!defaultSubRoleCategoryItems.Contains(subRole.SubRoleCategoryItem) &&  | ||||
|                     !unusedSubRoleCategoryItems.Contains(subRole.SubRoleCategoryItem)) { | ||||
|                     availableJobTitles.Add(subRole.SubRoleCategoryItem); | ||||
|  | ||||
|                     IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); | ||||
|  | ||||
|                     jobTitleToAvailableUsersMap.Add(subRole.SubRoleCategoryItem, subRoleMembers); | ||||
|  | ||||
|                     foreach (User member in subRoleMembers) { | ||||
|                         jobTitleToSubRoleMap.Add($"{subRole.SubRoleCategoryItem}{member.UserID}", subRole); | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 if (!unusedSubRoleCategoryItems.Contains(subRole.SubRoleCategoryItem)) { | ||||
|                     IEnumerable<User> subRoleMembers = await approvalService.GetApprovalGroupMembers(subRole.SubRoleID); | ||||
|  | ||||
|                     jobTitleToAvailableUsersMap.Add(subRole.SubRoleCategoryItem, subRoleMembers); | ||||
|  | ||||
|                     foreach (User member in subRoleMembers) | ||||
|                         availableUsers.Add(member); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void SelectedJobTitleChanged(string jobTitle) { | ||||
|         selectedJobTitle = jobTitle; | ||||
|  | ||||
|         selectedUser = null; | ||||
|  | ||||
|         availableUsers.Clear(); | ||||
|  | ||||
|         if (approval is null) { | ||||
|             if (jobTitleToAvailableUsersMap.TryGetValue(jobTitle, out IEnumerable<User>? jobTitleMembers)) { | ||||
|                 if (jobTitleMembers is not null) { | ||||
|                     foreach (User member in jobTitleMembers) | ||||
|                         availableUsers.Add(member); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             foreach (IEnumerable<User> memberList in jobTitleToAvailableUsersMap.Values) { | ||||
|                 if (memberList is not null) { | ||||
|                     foreach(User member in memberList) { | ||||
|                         availableUsers.Add(member); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         StateHasChanged(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										140
									
								
								MesaFabApproval.Client/Pages/Components/PCRBAttachmentForm.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								MesaFabApproval.Client/Pages/Components/PCRBAttachmentForm.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,140 @@ | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
| @inject NavigationManager navigationManager | ||||
| @inject IPCRBService pcrbService | ||||
| @inject ISnackbar snackbar | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         <MudPaper Class="m-2 p-2"> | ||||
|             <MudForm @bind-Errors="@errors"> | ||||
|                 <MudTextField T="string" | ||||
|                               Label="File Name" | ||||
|                               Disabled | ||||
|                               Immediate | ||||
|                               @bind-Value="@fileName" | ||||
|                               @bind-Text="@fileName" | ||||
|                               AutoGrow /> | ||||
|                 <MudFileUpload T="IBrowserFile"  | ||||
|                                FilesChanged="AddFile" | ||||
|                                Required | ||||
|                                Disabled="@(!string.IsNullOrWhiteSpace(fileName))" | ||||
|                                RequiredError="You must select a file"> | ||||
|                     <ActivatorContent> | ||||
|                         <MudButton Variant="Variant.Filled" | ||||
|                                    Color="Color.Tertiary" | ||||
|                                    style="margin: auto;" | ||||
|                                    StartIcon="@Icons.Material.Filled.AttachFile"> | ||||
|                             @if (addFileInProcess) { | ||||
|                                 <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                                 <MudText>Processing</MudText> | ||||
|                             } else { | ||||
|                                 <MudText>Add File</MudText> | ||||
|                             } | ||||
|                         </MudButton> | ||||
|                     </ActivatorContent> | ||||
|                 </MudFileUpload> | ||||
|             </MudForm> | ||||
|         </MudPaper> | ||||
|     </DialogContent> | ||||
|     <DialogActions> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Color="Color.Tertiary" | ||||
|                    Class="m1-auto" | ||||
|                    OnClick=Submit> | ||||
|             @if (processing) { | ||||
|                 <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|                 <MudText>Processing</MudText> | ||||
|             } | ||||
|             else { | ||||
|                 <MudText>Submit</MudText> | ||||
|             } | ||||
|         </MudButton> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Class="grey text-black m1-auto" | ||||
|                    OnClick=Cancel> | ||||
|             Cancel | ||||
|         </MudButton> | ||||
|     </DialogActions> | ||||
| </MudDialog> | ||||
|  | ||||
| @code { | ||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } | ||||
|  | ||||
|     [Parameter] | ||||
|     public int planNumber { get; set; } = 0; | ||||
|  | ||||
|     [Parameter] | ||||
|     public int step { get; set; } = 0; | ||||
|  | ||||
|     private string[] errors = { }; | ||||
|  | ||||
|     private string fileName = ""; | ||||
|  | ||||
|     private IBrowserFile? file = null; | ||||
|  | ||||
|     private bool addFileInProcess = false; | ||||
|     private bool processing = false; | ||||
|  | ||||
|     protected override async Task OnParametersSetAsync() { | ||||
|         if (planNumber <= 0) { | ||||
|             snackbar.Add($"{planNumber} is not a valid PCRB plan#", Severity.Error); | ||||
|             MudDialog.Close(DialogResult.Cancel()); | ||||
|         } | ||||
|  | ||||
|         if (step <= 0) { | ||||
|             snackbar.Add($"{step} is not a valid PCRB stage#", Severity.Error); | ||||
|             MudDialog.Close(DialogResult.Cancel()); | ||||
|         } | ||||
|  | ||||
|         if (authStateProvider.CurrentUser is null) { | ||||
|             await authStateProvider.Logout(); | ||||
|             navigationManager.NavigateTo("login"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void AddFile(IBrowserFile newFile) { | ||||
|         addFileInProcess = true; | ||||
|         file = newFile; | ||||
|         fileName = newFile.Name; | ||||
|         addFileInProcess = false; | ||||
|     } | ||||
|  | ||||
|     private async Task Submit() { | ||||
|         processing = true; | ||||
|         try { | ||||
|             if (authStateProvider.CurrentUser is null) { | ||||
|                 await authStateProvider.Logout(); | ||||
|                 navigationManager.NavigateTo("login"); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             if (string.IsNullOrWhiteSpace(fileName)) | ||||
|                 throw new Exception("file name missing"); | ||||
|  | ||||
|             if (file is null) | ||||
|                 throw new Exception("file is missing"); | ||||
|  | ||||
|             PCRBAttachment attachment = new() { | ||||
|                 Step = step, | ||||
|                 UploadDateTime = DateTime.Now, | ||||
|                 UploadedByID = authStateProvider.CurrentUser.UserID, | ||||
|                 PlanNumber = planNumber, | ||||
|                 File = file, | ||||
|                 FileName = fileName | ||||
|             }; | ||||
|  | ||||
|             await pcrbService.UploadAttachment(attachment); | ||||
|  | ||||
|             processing = false; | ||||
|  | ||||
|             MudDialog.Close(DialogResult.Ok(attachment)); | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add($"Unable to save document, because {ex.Message}", Severity.Error); | ||||
|             processing = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void Cancel() { | ||||
|         MudDialog.Close(DialogResult.Cancel()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										74
									
								
								MesaFabApproval.Client/Pages/Components/UserSelector.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								MesaFabApproval.Client/Pages/Components/UserSelector.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | ||||
| @inject IUserService userService | ||||
| @inject ISnackbar snackbar | ||||
| @inject MesaFabApprovalAuthStateProvider authStateProvider | ||||
|  | ||||
| <MudDialog> | ||||
|     <DialogContent> | ||||
|         @if (allUsers is not null) { | ||||
|             <MudPaper Class="p-2"> | ||||
|                 <MudSelect T="User" | ||||
|                            Label="Select a User" | ||||
|                            Required | ||||
|                            Variant="Variant.Outlined" | ||||
|                            AnchorOrigin="Origin.BottomCenter" | ||||
|                            @bind-Value=selectedUser | ||||
|                            Text="@(selectedUser is null ? "" : selectedUser.GetFullName())"> | ||||
|                     @foreach (User user in allUsers) { | ||||
|                         <MudSelectItem Value="@user"> | ||||
|                             @user.GetFullName() | ||||
|                         </MudSelectItem> | ||||
|                     } | ||||
|                 </MudSelect> | ||||
|             </MudPaper> | ||||
|         } | ||||
|     </DialogContent> | ||||
|     <DialogActions> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Color="Color.Tertiary" | ||||
|                    Class="m1-auto" | ||||
|                    OnClick=Submit> | ||||
|             <MudText>Submit</MudText> | ||||
|         </MudButton> | ||||
|         <MudButton Variant="Variant.Filled" | ||||
|                    Color="Color.Secondary" | ||||
|                    Class="m1-auto" | ||||
|                    OnClick=Cancel> | ||||
|             <MudText>Cancel</MudText> | ||||
|         </MudButton> | ||||
|     </DialogActions> | ||||
| </MudDialog> | ||||
|  | ||||
| <MudOverlay Visible=processing DarkBackground="true" AutoClose="false"> | ||||
|     <MudProgressCircular Color="Color.Info" Size="Size.Medium" Indeterminate="true" /> | ||||
| </MudOverlay> | ||||
|  | ||||
| @code { | ||||
|     [CascadingParameter] MudDialogInstance MudDialog { get; set; } | ||||
|  | ||||
|     [Parameter] | ||||
|     public User selectedUser { get; set; } | ||||
|  | ||||
|     private bool processing = false; | ||||
|  | ||||
|     private IEnumerable<User> allUsers = new List<User>(); | ||||
|  | ||||
|     protected override async Task OnInitializedAsync() { | ||||
|         try { | ||||
|             processing = true; | ||||
|             selectedUser = authStateProvider.CurrentUser; | ||||
|             allUsers = await userService.GetAllActiveUsers(); | ||||
|             processing = false; | ||||
|         } catch (Exception ex) { | ||||
|             processing = false; | ||||
|             snackbar.Add($"Unable to get all users, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void Submit() { | ||||
|         MudDialog.Close(DialogResult.Ok(selectedUser)); | ||||
|     } | ||||
|  | ||||
|     private void Cancel() { | ||||
|         MudDialog.Close(DialogResult.Cancel()); | ||||
|     } | ||||
| } | ||||
| @ -1,77 +1,363 @@ | ||||
| @page "/" | ||||
| @page "/Dashboard" | ||||
| @inject IConfiguration Configuration | ||||
| @inject MesaFabApprovalAuthStateProvider stateProvider | ||||
| @inject IApprovalService approvalService | ||||
| @inject IMemoryCache cache | ||||
| @inject NavigationManager navigationManager | ||||
| @inject ISnackbar snackbar | ||||
| @inject IMRBService mrbService | ||||
| @inject IPCRBService pcrbService | ||||
| @inject IECNService ecnService | ||||
| @inject ICAService caService | ||||
| @inject IJSRuntime jsRuntime | ||||
|  | ||||
| <PageTitle>Dashboard</PageTitle> | ||||
|  | ||||
| <MudPaper Class="p-2 m-2" MinWidth="100%"> | ||||
| <MudPaper Class="p-2 m-2"> | ||||
|     <MudText Typo="Typo.h3" Align="Align.Center">Dashboard</MudText> | ||||
| </MudPaper> | ||||
|  | ||||
| @if (stateProvider.CurrentUser is not null && approvalList is not null) { | ||||
|     <MudPaper Outlined="true" | ||||
|               Class="p-2 m-2 d-flex flex-column justify-center"> | ||||
|         <MudText Typo="Typo.h4" Align="Align.Center">My Active Approvals</MudText> | ||||
|         <MudDivider DividerType="DividerType.Middle" Class="my-2" /> | ||||
|         <MudTable Items="@approvalList" | ||||
|                   Class="m-2" | ||||
|                   Striped | ||||
|                   SortLabel="Sort by"> | ||||
|             <HeaderContent> | ||||
|                 <MudTh> | ||||
|                     <MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<Approval,object>(x=>x.IssueID)"> | ||||
|                         Issue ID | ||||
|                     </MudTableSortLabel> | ||||
|                 </MudTh> | ||||
|                 <MudTh> | ||||
|                     <MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<Approval,object>(x=>x.SubRoleCategoryItem)"> | ||||
|                         Role | ||||
|                     </MudTableSortLabel> | ||||
|                 </MudTh> | ||||
|                 <MudTh> | ||||
|                     <MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<Approval,object>(x=>x.AssignedDate)"> | ||||
|                         Assigned Date | ||||
|                     </MudTableSortLabel> | ||||
|                 </MudTh> | ||||
|                 <MudTh> | ||||
|                     <MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<Approval,object>(x=>x.Step)"> | ||||
|                         Step | ||||
|                     </MudTableSortLabel> | ||||
|                 </MudTh> | ||||
|             </HeaderContent> | ||||
|             <RowTemplate> | ||||
|                 <MudTd DataLabel="Issue ID"> | ||||
|                     <MudLink OnClick="@(() => GoTo($"/mrb/{context.IssueID}"))">@context.IssueID</MudLink> | ||||
|                 </MudTd> | ||||
|                 <MudTd DataLabel="Role">@context.SubRoleCategoryItem</MudTd> | ||||
|                 <MudTd DataLabel="Assigned Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.AssignedDate)</MudTd> | ||||
|                 <MudTd DataLabel="Step">@context.Step</MudTd> | ||||
|             </RowTemplate> | ||||
|             <PagerContent> | ||||
|                 <MudTablePager /> | ||||
|             </PagerContent> | ||||
|         </MudTable> | ||||
|     </MudPaper> | ||||
| } | ||||
| <MudPaper Class="p-2 m-2"> | ||||
|     <MudTabs Class="p-2" Rounded Centered Color="Color.Info" MinimumTabWidth="50%"> | ||||
|         <MudTabPanel Text="My Active Approvals" Style="min-width:50%; text-align: center;"> | ||||
|             @if (stateProvider.CurrentUser is not null && approvalList is not null && !myApprovalsProcessing) { | ||||
|                 <MudPaper Outlined="true" | ||||
|                           Class="p-2 m-2 d-flex flex-column justify-center"> | ||||
|                     <MudText Typo="Typo.h4" Align="Align.Center">My Active Approvals</MudText> | ||||
|                     <MudDivider DividerType="DividerType.Middle" Class="my-2" /> | ||||
|                     <MudTable Items="@approvalList" | ||||
|                               Class="m-2" | ||||
|                               Striped | ||||
|                               SortLabel="Sort by"> | ||||
|                         <HeaderContent> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.IssueID)"> | ||||
|                                     Issue ID | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.SubRoleCategoryItem)"> | ||||
|                                     Role | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<Approval,object>(x=>x.AssignedDate)"> | ||||
|                                     Assigned Date | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<Approval,object>(x=>x.Step)"> | ||||
|                                     Step | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                         </HeaderContent> | ||||
|                         <RowTemplate> | ||||
|                             <MudTd DataLabel="Issue ID"> | ||||
|                                 @if (context.IssueID > 0) { | ||||
|                                     <MudLink OnClick="@(() => FollowLink(context.IssueID))">@context.IssueID</MudLink> | ||||
|                                 } | ||||
|                             </MudTd> | ||||
|                             <MudTd DataLabel="Role">@context.SubRoleCategoryItem</MudTd> | ||||
|                             <MudTd DataLabel="Assigned Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.AssignedDate)</MudTd> | ||||
|                             <MudTd DataLabel="Step">@context.Step</MudTd> | ||||
|                         </RowTemplate> | ||||
|                         <PagerContent> | ||||
|                             <MudTablePager /> | ||||
|                         </PagerContent> | ||||
|                     </MudTable> | ||||
|                 </MudPaper> | ||||
|             } else { | ||||
|                 <MudOverlay Visible DarkBackground="true" AutoClose="false"> | ||||
|                     <MudText Align="Align.Center" Typo="Typo.h3">Processing</MudText> | ||||
|                     <MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" /> | ||||
|                 </MudOverlay> | ||||
|             } | ||||
|         </MudTabPanel> | ||||
|         <MudTabPanel Text="My MRBs" Style="min-width:50%; text-align: center;"> | ||||
|             @if (stateProvider.CurrentUser is not null && myMRBs is not null && !myMrbsProcessing) { | ||||
|                 <MudPaper Outlined="true" | ||||
|                           Class="p-2 m-2 d-flex flex-column justify-center"> | ||||
|                     <MudText Typo="Typo.h4" Align="Align.Center">My MRBs</MudText> | ||||
|                     <MudDivider DividerType="DividerType.Middle" Class="my-2" /> | ||||
|                     <MudTable Items="@myMRBs" | ||||
|                               Class="m-2" | ||||
|                               Striped | ||||
|                               SortLabel="Sort by" | ||||
|                               Filter="new Func<MRB, bool>(FilterFuncForMRBTable)"> | ||||
|                         <ToolBarContent> | ||||
|                             <MudSpacer /> | ||||
|                             <MudTextField @bind-Value="mrbSearchString" | ||||
|                                           Placeholder="Search" | ||||
|                                           Immediate | ||||
|                                           Adornment="Adornment.Start" | ||||
|                                           AdornmentIcon="@Icons.Material.Filled.Search" | ||||
|                                           IconSize="Size.Medium" | ||||
|                                           Class="mt-0" /> | ||||
|                         </ToolBarContent> | ||||
|                         <HeaderContent> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<MRB,object>(x=>x.MRBNumber)"> | ||||
|                                     MRB# | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.Title)"> | ||||
|                                     Title | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.SubmittedDate)"> | ||||
|                                     Submitted Date | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.ApprovalDate)"> | ||||
|                                     Approval Date | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.CancelDate)"> | ||||
|                                     Cancel Date | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.CloseDate)"> | ||||
|                                     Completed Date | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                         </HeaderContent> | ||||
|                         <RowTemplate> | ||||
|                             <MudTd DataLabel="MRB#"> | ||||
|                                 <MudLink OnClick="@(() => GoTo($"mrb/{context.MRBNumber}"))">@context.MRBNumber</MudLink> | ||||
|                             </MudTd> | ||||
|                             <MudTd DataLabel="Title">@context.Title</MudTd> | ||||
|                             <MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.SubmittedDate)</MudTd> | ||||
|                             <MudTd DataLabel="Approval Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ApprovalDate)</MudTd> | ||||
|                             <MudTd DataLabel="Cancel Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CancelDate)</MudTd> | ||||
|                             <MudTd DataLabel="Completed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CloseDate)</MudTd> | ||||
|                         </RowTemplate> | ||||
|                         <PagerContent> | ||||
|                             <MudTablePager /> | ||||
|                         </PagerContent> | ||||
|                     </MudTable> | ||||
|                 </MudPaper> | ||||
|             } else { | ||||
|                 <MudOverlay Visible DarkBackground="true" AutoClose="false"> | ||||
|                     <MudText Align="Align.Center" Typo="Typo.h3">Processing</MudText> | ||||
|                     <MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" /> | ||||
|                 </MudOverlay> | ||||
|             } | ||||
|         </MudTabPanel> | ||||
|         <MudTabPanel Text="My PCRBs" Style="min-width:50%; text-align: center;"> | ||||
|             @if (stateProvider.CurrentUser is not null && myPCRBs is not null && !myPcrbsProcessing) { | ||||
|                 <MudPaper Outlined="true" | ||||
|                           Class="p-2 m-2 d-flex flex-column justify-center"> | ||||
|                     <MudText Typo="Typo.h4" Align="Align.Center">My PCRBs</MudText> | ||||
|                     <MudDivider DividerType="DividerType.Middle" Class="my-2" /> | ||||
|                     <MudTable Items="@myPCRBs" | ||||
|                               Class="m-2" | ||||
|                               Striped | ||||
|                               SortLabel="Sort by" | ||||
|                               Filter="new Func<PCRB, bool>(FilterFuncForPCRBTable)"> | ||||
|                         <ToolBarContent> | ||||
|                             <MudSpacer /> | ||||
|                             <MudTextField @bind-Value="pcrbSearchString" | ||||
|                                           Placeholder="Search" | ||||
|                                           Adornment="Adornment.Start" | ||||
|                                           Immediate | ||||
|                                           AdornmentIcon="@Icons.Material.Filled.Search" | ||||
|                                           IconSize="Size.Medium" | ||||
|                                           Class="mt-0" /> | ||||
|                         </ToolBarContent> | ||||
|                         <HeaderContent> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<PCRB,object>(x=>x.PlanNumber)"> | ||||
|                                     PCRB# | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.Title)"> | ||||
|                                     Title | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.CurrentStep)"> | ||||
|                                     Current Step | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.InsertTimeStamp)"> | ||||
|                                     Submitted Date | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.LastUpdateDate)"> | ||||
|                                     Last Updated | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                             <MudTh> | ||||
|                                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.ClosedDate)"> | ||||
|                                     Completed Date | ||||
|                                 </MudTableSortLabel> | ||||
|                             </MudTh> | ||||
|                         </HeaderContent> | ||||
|                         <RowTemplate> | ||||
|                             <MudTd DataLabel="PCRB#"> | ||||
|                                 <MudLink OnClick="@(() => GoTo($"pcrb/{context.PlanNumber}"))">@context.PlanNumber</MudLink> | ||||
|                             </MudTd> | ||||
|                             <MudTd DataLabel="Title">@context.Title</MudTd> | ||||
|                             <MudTd DataLabel="Current Step">@(GetCurrentPCRBStep(context.CurrentStep))</MudTd> | ||||
|                             <MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.InsertTimeStamp)</MudTd> | ||||
|                             <MudTd DataLabel="Last Updated">@DateTimeUtilities.GetDateAsStringMinDefault(context.LastUpdateDate)</MudTd> | ||||
|                             <MudTd DataLabel="Completed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ClosedDate)</MudTd> | ||||
|                         </RowTemplate> | ||||
|                         <PagerContent> | ||||
|                             <MudTablePager /> | ||||
|                         </PagerContent> | ||||
|                     </MudTable> | ||||
|                 </MudPaper> | ||||
|             } else { | ||||
|                 <MudOverlay Visible DarkBackground="true" AutoClose="false"> | ||||
|                     <MudText Align="Align.Center" Typo="Typo.h3">Processing</MudText> | ||||
|                     <MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" /> | ||||
|                 </MudOverlay> | ||||
|             } | ||||
|         </MudTabPanel> | ||||
|     </MudTabs> | ||||
| </MudPaper> | ||||
|  | ||||
| @code { | ||||
|     private IEnumerable<Approval> approvalList = new List<Approval>(); | ||||
|     private IEnumerable<MRB> myMRBs = new List<MRB>(); | ||||
|     private IEnumerable<PCRB> myPCRBs = new List<PCRB>(); | ||||
|  | ||||
|     private IEnumerable<int> ecnNumbers = new HashSet<int>(); | ||||
|     private IEnumerable<int> caNumbers = new HashSet<int>(); | ||||
|     private IEnumerable<int> mrbNumbers = new HashSet<int>(); | ||||
|     private IEnumerable<int> pcrbNumbers = new HashSet<int>(); | ||||
|  | ||||
|     private bool myApprovalsProcessing = false; | ||||
|     private bool myMrbsProcessing = false; | ||||
|     private bool myPcrbsProcessing = false; | ||||
|  | ||||
|     private string mrbSearchString = ""; | ||||
|     private string pcrbSearchString = ""; | ||||
|  | ||||
|     protected async override Task OnParametersSetAsync() { | ||||
|         try { | ||||
|             if (stateProvider.CurrentUser is not null) | ||||
|                 approvalList = (await approvalService.GetApprovalsForUserId(stateProvider.CurrentUser.UserID, true)).ToList(); | ||||
|             if (stateProvider.CurrentUser is not null) { | ||||
|                 myApprovalsProcessing = true; | ||||
|                 approvalList = (await approvalService.GetApprovalsForUserId(stateProvider.CurrentUser.UserID, true)) | ||||
|                     .Where(a => a.CompletedDate > DateTime.Now && a.ItemStatus == 0) | ||||
|                     .ToList() | ||||
|                     .OrderByDescending(x => x.AssignedDate); | ||||
|                 myApprovalsProcessing = false; | ||||
|  | ||||
|                 myMrbsProcessing = true; | ||||
|                 myMRBs = (await mrbService.GetAllMRBs(false)).Where(m => m.OriginatorID == stateProvider.CurrentUser.UserID) | ||||
|                     .ToList() | ||||
|                     .OrderByDescending(x => x.SubmittedDate); | ||||
|                 myMrbsProcessing = false; | ||||
|  | ||||
|                 myPcrbsProcessing = true; | ||||
|                 myPCRBs = (await pcrbService.GetAllPCRBs(false)).Where(p => p.OwnerID == stateProvider.CurrentUser.UserID) | ||||
|                     .ToList() | ||||
|                     .OrderByDescending(p => p.InsertTimeStamp); | ||||
|                 myPcrbsProcessing = false; | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add($"Unable to fetch your outstanding approvals, because {ex.Message}", Severity.Error); | ||||
|             myApprovalsProcessing = false; | ||||
|             myMrbsProcessing = false; | ||||
|             myPcrbsProcessing = false; | ||||
|             snackbar.Add($"Unable to load the dashboard, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task FollowLink(int issueId) { | ||||
|         HashSet<Task> tasks = new(); | ||||
|  | ||||
|         bool isEcn = false; | ||||
|         bool isCa = false; | ||||
|         bool isMrb = false; | ||||
|         bool isPcrb = false; | ||||
|         if (ecnNumbers.Contains(issueId)) | ||||
|             isEcn = true; | ||||
|         if (caNumbers.Contains(issueId)) | ||||
|             isCa = true; | ||||
|         if (mrbNumbers.Contains(issueId)) | ||||
|             isMrb = true; | ||||
|         if (pcrbNumbers.Contains(issueId)) | ||||
|             isPcrb = true; | ||||
|  | ||||
|         if (!isEcn && !isCa && !isMrb) { | ||||
|             Task<bool> isEcnTask = ecnService.ECNNumberIsValid(issueId); | ||||
|             tasks.Add(isEcnTask); | ||||
|  | ||||
|             Task<bool> isCaTask = caService.CANumberIsValid(issueId); | ||||
|             tasks.Add(isCaTask); | ||||
|  | ||||
|             Task<bool> isMrbTask = mrbService.NumberIsValid(issueId); | ||||
|             tasks.Add(isMrbTask); | ||||
|  | ||||
|             Task<bool> isPcrbTask = pcrbService.IdIsValid(issueId); | ||||
|             tasks.Add(isPcrbTask); | ||||
|  | ||||
|             await Task.WhenAll(tasks); | ||||
|  | ||||
|             if (isEcnTask.Result) { | ||||
|                 isEcn = true; | ||||
|             } else if (isCaTask.Result) { | ||||
|                 isCa = true; | ||||
|             } else if (isMrbTask.Result) { | ||||
|                 isMrb = true; | ||||
|             } else if (isPcrbTask.Result) { | ||||
|                 isPcrb = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (isEcn) await GoToExternal($"{Configuration["OldFabApprovalUrl"]}/ECN/Edit?IssueID={issueId}", ""); | ||||
|         if (isCa) await GoToExternal($"{Configuration["OldFabApprovalUrl"]}/CorrectiveAction/Edit?IssueID={issueId}", ""); | ||||
|         if (isMrb) GoTo($"mrb/{issueId}"); | ||||
|         if (isPcrb) GoTo($"pcrb/{issueId}"); | ||||
|     } | ||||
|  | ||||
|     private void GoTo(string page) { | ||||
|         cache.Set("redirectUrl", page); | ||||
|         navigationManager.NavigateTo(page); | ||||
|         navigationManager.NavigateTo("/" + page); | ||||
|     } | ||||
|  | ||||
|     private async Task GoToExternal(string url, string content) { | ||||
|         IJSObjectReference windowModule = await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./js/OpenInNewWindow.js"); | ||||
|         await windowModule.InvokeAsync<object>("OpenInNewWindow", url, content); | ||||
|     } | ||||
|  | ||||
|     private bool FilterFuncForMRBTable(MRB mrb) => MRBFilterFunc(mrb, mrbSearchString); | ||||
|  | ||||
|     private bool MRBFilterFunc(MRB mrb, string searchString) { | ||||
|         if (string.IsNullOrWhiteSpace(searchString)) | ||||
|             return true; | ||||
|         if (mrb.Title.ToLower().Contains(searchString.Trim().ToLower())) | ||||
|             return true; | ||||
|         if (mrb.MRBNumber.ToString().Contains(searchString.Trim())) | ||||
|             return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private bool FilterFuncForPCRBTable(PCRB pcrb) => PCRBFilterFunc(pcrb, pcrbSearchString); | ||||
|  | ||||
|     private bool PCRBFilterFunc(PCRB pcrb, string searchString) { | ||||
|         if (string.IsNullOrWhiteSpace(searchString)) | ||||
|             return true; | ||||
|         if (pcrb.Title.ToLower().Contains(searchString.Trim().ToLower())) | ||||
|             return true; | ||||
|         if (pcrb.PlanNumber.ToString().Contains(searchString.Trim())) | ||||
|             return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private string GetCurrentPCRBStep(int step) { | ||||
|         if (step < 0 || step > (PCRB.Stages.Length - 1)) return string.Empty; | ||||
|         else return PCRB.Stages[step]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -45,6 +45,19 @@ | ||||
|                 <MudText>Log In</MudText> | ||||
|             } | ||||
|         </MudButton> | ||||
|         <MudDivider /> | ||||
|         @* <MudButton | ||||
|             Variant="Variant.Filled" | ||||
|             Color="Color.Tertiary" | ||||
|             Class="m-1" | ||||
|             OnClick="LoginLocal" > | ||||
|         @if (processingLocal) { | ||||
|             <MudProgressCircular Class="m-1" Size="Size.Small" Indeterminate="true" /> | ||||
|             <MudText>Processing</MudText> | ||||
|         } else { | ||||
|             <MudText>Log In (SSO)</MudText> | ||||
|         } | ||||
|         </MudButton> *@ | ||||
|     </MudForm> | ||||
| </MudPaper> | ||||
|  | ||||
| @ -55,6 +68,7 @@ | ||||
|     public string? redirectUrlSub { get; set; } | ||||
|     private bool success; | ||||
|     private bool processing = false; | ||||
|     private bool processingLocal = false; | ||||
|     private string[] errors = { }; | ||||
|     private string? username; | ||||
|     private string? password; | ||||
| @ -81,4 +95,19 @@ | ||||
|             SubmitLogin(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task LoginLocal() { | ||||
|         processingLocal = true; | ||||
|  | ||||
|         await authStateProvider.LoginLocal(); | ||||
|         if (!string.IsNullOrWhiteSpace(redirectUrl) && !string.IsNullOrWhiteSpace(redirectUrlSub)) { | ||||
|             navManager.NavigateTo($"{redirectUrl}/{redirectUrlSub}"); | ||||
|         } else if (!string.IsNullOrWhiteSpace(redirectUrl)) { | ||||
|             navManager.NavigateTo(redirectUrl); | ||||
|         } else { | ||||
|             navManager.NavigateTo("dashboard"); | ||||
|         } | ||||
|  | ||||
|         processingLocal = false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -48,6 +48,11 @@ | ||||
|                     Submitted Date | ||||
|                 </MudTableSortLabel> | ||||
|             </MudTh> | ||||
|             <MudTh> | ||||
|                 <MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.ApprovalDate)"> | ||||
|                     Approval Date | ||||
|                 </MudTableSortLabel> | ||||
|             </MudTh> | ||||
|             <MudTh> | ||||
|                 <MudTableSortLabel SortBy="new Func<MRB,object>(x=>x.CloseDate)"> | ||||
|                     Closed Date | ||||
| @ -56,11 +61,12 @@ | ||||
|         </HeaderContent> | ||||
|         <RowTemplate> | ||||
|             <MudTd DataLabel="MRB#"> | ||||
|                 <MudLink OnClick="@(() => GoTo($"/mrb/{context.MRBNumber}"))">@context.MRBNumber</MudLink> | ||||
|                 <MudLink OnClick="@(() => GoTo($"mrb/{context.MRBNumber}"))">@context.MRBNumber</MudLink> | ||||
|             </MudTd> | ||||
|             <MudTd DataLabel="Title">@context.Title</MudTd> | ||||
|             <MudTd DataLabel="Originator">@context.OriginatorName</MudTd> | ||||
|             <MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.SubmittedDate)</MudTd> | ||||
|             <MudTd DataLabel="Approval Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ApprovalDate)</MudTd> | ||||
|             <MudTd DataLabel="Closed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.CloseDate)</MudTd> | ||||
|         </RowTemplate> | ||||
|         <PagerContent> | ||||
| @ -84,7 +90,7 @@ | ||||
|             if (mrbService is null) { | ||||
|                 throw new Exception("MRB service not injected!"); | ||||
|             } else { | ||||
|                 allMrbs = await mrbService.GetAllMRBs(); | ||||
|                 allMrbs = await mrbService.GetAllMRBs(false); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
| @ -97,11 +103,11 @@ | ||||
|     private bool FilterFunc(MRB mrb, string searchString) { | ||||
|         if (string.IsNullOrWhiteSpace(searchString)) | ||||
|             return true; | ||||
|         if (mrb.Title.ToLower().Contains(searchString.ToLower())) | ||||
|         if (mrb.Title.ToLower().Contains(searchString.Trim().ToLower())) | ||||
|             return true; | ||||
|         if (mrb.OriginatorName.ToLower().Contains(searchString.ToLower())) | ||||
|         if (mrb.OriginatorName.ToLower().Contains(searchString.Trim().ToLower())) | ||||
|             return true; | ||||
|         if (mrb.MRBNumber.ToString().Contains(searchString)) | ||||
|         if (mrb.MRBNumber.ToString().Contains(searchString.Trim())) | ||||
|             return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										126
									
								
								MesaFabApproval.Client/Pages/PCRBAll.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								MesaFabApproval.Client/Pages/PCRBAll.razor
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,126 @@ | ||||
| @page "/pcrb/all" | ||||
| @using System.Globalization | ||||
| @inject IPCRBService pcrbService | ||||
| @inject ISnackbar snackbar | ||||
| @inject IMemoryCache cache | ||||
| @inject NavigationManager navigationManager | ||||
|  | ||||
| <PageTitle>PCRB</PageTitle> | ||||
|  | ||||
| <MudPaper Class="p-2 m-2"> | ||||
|     <MudText Typo="Typo.h3" Align="Align.Center">PCRB List</MudText> | ||||
| </MudPaper> | ||||
|  | ||||
| @if (allPCRBs is not null && allPCRBs.Count() > 0) { | ||||
|     <MudTable Items="@allPCRBs" | ||||
|               Class="m-2" | ||||
|               Striped="true" | ||||
|               Filter="new Func<PCRB,bool>(FilterFuncForTable)" | ||||
|               SortLabel="Sort By" | ||||
|               Hover="true"> | ||||
|         <ToolBarContent> | ||||
|             <MudSpacer /> | ||||
|             <MudTextField @bind-Value="searchString" | ||||
|                           Placeholder="Search" | ||||
|                           Adornment="Adornment.Start" | ||||
|                           AdornmentIcon="@Icons.Material.Filled.Search" | ||||
|                           IconSize="Size.Medium" | ||||
|                           Class="mt-0" /> | ||||
|         </ToolBarContent> | ||||
|         <HeaderContent> | ||||
|             <MudTh> | ||||
|                 <MudTableSortLabel InitialDirection="SortDirection.Descending" SortBy="new Func<PCRB,object>(x=>x.PlanNumber)"> | ||||
|                     Change# | ||||
|                 </MudTableSortLabel> | ||||
|             </MudTh> | ||||
|             <MudTh> | ||||
|                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.Title)"> | ||||
|                     Title | ||||
|                 </MudTableSortLabel> | ||||
|             </MudTh> | ||||
|             <MudTh> | ||||
|                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.OwnerName)"> | ||||
|                     Owner | ||||
|                 </MudTableSortLabel> | ||||
|             </MudTh> | ||||
|             <MudTh>Stage</MudTh> | ||||
|             <MudTh> | ||||
|                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.InsertTimeStamp)"> | ||||
|                     Submitted Date | ||||
|                 </MudTableSortLabel> | ||||
|             </MudTh> | ||||
|             <MudTh> | ||||
|                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.LastUpdateDate)"> | ||||
|                     Last Updated | ||||
|                 </MudTableSortLabel> | ||||
|             </MudTh> | ||||
|             <MudTh> | ||||
|                 <MudTableSortLabel SortBy="new Func<PCRB,object>(x=>x.ClosedDate)"> | ||||
|                     Closed Date | ||||
|                 </MudTableSortLabel> | ||||
|             </MudTh> | ||||
|         </HeaderContent> | ||||
|         <RowTemplate> | ||||
|             <MudTd DataLabel="Plan#"> | ||||
|                 <MudLink OnClick="@(() => GoTo($"pcrb/{context.PlanNumber.ToString()}"))">@context.PlanNumber</MudLink> | ||||
|             </MudTd> | ||||
|             <MudTd DataLabel="Title">@context.Title</MudTd> | ||||
|             <MudTd DataLabel="Owner">@context.OwnerName</MudTd> | ||||
|             <MudTd DataLabel="Stage">@(GetStageName(context.CurrentStep))</MudTd> | ||||
|             <MudTd DataLabel="Submitted Date">@DateTimeUtilities.GetDateAsStringMinDefault(context.InsertTimeStamp)</MudTd> | ||||
|             <MudTd DataLabel="Last Updated">@DateTimeUtilities.GetDateAsStringMinDefault(context.LastUpdateDate)</MudTd> | ||||
|             <MudTd DataLabel="Closed Date">@DateTimeUtilities.GetDateAsStringMaxDefault(context.ClosedDate)</MudTd> | ||||
|         </RowTemplate> | ||||
|         <PagerContent> | ||||
|             <MudTablePager /> | ||||
|         </PagerContent> | ||||
|     </MudTable> | ||||
| } | ||||
|  | ||||
| <MudOverlay @bind-Visible=inProcess DarkBackground="true" AutoClose="false"> | ||||
|     <MudProgressCircular Color="Color.Info" Size="Size.Large" Indeterminate="true" /> | ||||
| </MudOverlay> | ||||
|  | ||||
| @code { | ||||
|     private bool inProcess = false; | ||||
|     private string searchString = ""; | ||||
|     private IEnumerable<PCRB> allPCRBs = new List<PCRB>(); | ||||
|  | ||||
|     protected override async Task OnParametersSetAsync() { | ||||
|         inProcess = true; | ||||
|         try { | ||||
|             if (pcrbService is null) { | ||||
|                 throw new Exception("PCRB service not injected!"); | ||||
|             } else { | ||||
|                 allPCRBs = await pcrbService.GetAllPCRBs(false); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             snackbar.Add(ex.Message, Severity.Error); | ||||
|         } | ||||
|         inProcess = false; | ||||
|     } | ||||
|  | ||||
|     private bool FilterFuncForTable(PCRB pcrb) => FilterFunc(pcrb, searchString); | ||||
|  | ||||
|     private bool FilterFunc(PCRB pcrb, string searchString) { | ||||
|         if (string.IsNullOrWhiteSpace(searchString)) | ||||
|             return true; | ||||
|         if (pcrb.Title.ToLower().Contains(searchString.Trim().ToLower())) | ||||
|             return true; | ||||
|         if (pcrb.OwnerName.ToLower().Contains(searchString.Trim().ToLower())) | ||||
|             return true; | ||||
|         if (pcrb.PlanNumber.ToString().Contains(searchString.Trim())) | ||||
|             return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private void GoTo(string page) { | ||||
|         cache.Set("redirectUrl", page); | ||||
|         navigationManager.NavigateTo(page); | ||||
|     } | ||||
|  | ||||
|     private string GetStageName(int step) { | ||||
|         if (step >= PCRB.Stages.Length || step < 0) return ""; | ||||
|         return PCRB.Stages[step]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1771
									
								
								MesaFabApproval.Client/Pages/PCRBSingle.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1771
									
								
								MesaFabApproval.Client/Pages/PCRBSingle.razor
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -9,17 +9,23 @@ using MesaFabApproval.Client.Services; | ||||
| using Microsoft.AspNetCore.Components.Authorization; | ||||
| using System.Net.Http.Headers; | ||||
| using MudBlazor; | ||||
| using Blazored.SessionStorage; | ||||
|  | ||||
| WebAssemblyHostBuilder builder = WebAssemblyHostBuilder.CreateDefault(args); | ||||
|  | ||||
| string _apiBaseUrl = builder.Configuration["FabApprovalApiBaseUrl"] ?? | ||||
|     throw new NullReferenceException("FabApprovalApiBaseUrl not found in config"); | ||||
|  | ||||
| builder.Services.AddBlazoredSessionStorage(); | ||||
| string _oldSiteUrl = builder.Configuration["OldFabApprovalUrl"] ?? | ||||
|     throw new NullReferenceException("OldFabApprovalUrl not found in config"); | ||||
|  | ||||
| builder.Services.AddTransient<ApiHttpClientHandler>(); | ||||
|  | ||||
| builder.Services | ||||
|     .AddHttpClient("API_Handler", client => { | ||||
|         client.BaseAddress = new Uri(_apiBaseUrl); | ||||
|         client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*")); | ||||
|     }); | ||||
|  | ||||
| builder.Services | ||||
|     .AddHttpClient("API", client => { | ||||
|         client.BaseAddress = new Uri(_apiBaseUrl); | ||||
| @ -27,6 +33,12 @@ builder.Services | ||||
|     }) | ||||
|     .AddHttpMessageHandler<ApiHttpClientHandler>(); | ||||
|  | ||||
| builder.Services | ||||
|     .AddHttpClient("OldSite", client => { | ||||
|         client.BaseAddress = new Uri(_oldSiteUrl); | ||||
|         client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*")); | ||||
|     }); | ||||
|  | ||||
| builder.Services.AddMemoryCache(); | ||||
|  | ||||
| builder.Services.AddMudServices(config => { | ||||
| @ -35,13 +47,18 @@ builder.Services.AddMudServices(config => { | ||||
|     config.SnackbarConfiguration.MaxDisplayedSnackbars = 5; | ||||
|     config.SnackbarConfiguration.SnackbarVariant = Variant.Filled; | ||||
|     config.SnackbarConfiguration.ShowCloseIcon = true; | ||||
|     config.SnackbarConfiguration.VisibleStateDuration = 5000; | ||||
|     config.SnackbarConfiguration.VisibleStateDuration = 7000; | ||||
|     config.SnackbarConfiguration.HideTransitionDuration = 500; | ||||
|     config.SnackbarConfiguration.ShowTransitionDuration = 500; | ||||
| }); | ||||
|  | ||||
| builder.Services.AddScoped<ILocalStorageService, LocalStorageService>(); | ||||
| builder.Services.AddScoped<IAuthenticationService, AuthenticationService>(); | ||||
| builder.Services.AddScoped<ICustomerService, CustomerService>(); | ||||
| builder.Services.AddScoped<IUserService, UserService>(); | ||||
| builder.Services.AddScoped<IECNService, ECNService>(); | ||||
| builder.Services.AddScoped<ICAService, CAService>(); | ||||
| builder.Services.AddScoped<IPCRBService, PCRBService>(); | ||||
| builder.Services.AddScoped<IMRBService, MRBService>(); | ||||
| builder.Services.AddScoped<IApprovalService, ApprovalService>(); | ||||
| builder.Services.AddScoped<MesaFabApprovalAuthStateProvider>(); | ||||
|  | ||||
| @ -193,9 +193,6 @@ public class ApprovalService : IApprovalService { | ||||
|         if (!responseMessage.IsSuccessStatusCode) { | ||||
|             throw new Exception($"Denial failed, because {responseMessage.ReasonPhrase}"); | ||||
|         } | ||||
|  | ||||
|         await GetApprovalsForIssueId(approval.IssueID, true); | ||||
|         await GetApprovalsForUserId(approval.UserID, true); | ||||
|     } | ||||
|  | ||||
|     public async Task<int> GetRoleIdForRoleName(string roleName) { | ||||
| @ -292,7 +289,7 @@ public class ApprovalService : IApprovalService { | ||||
|                 if (members is null || members.Count() <= 0) | ||||
|                     throw new Exception($"unable to find group members for sub role {subRoleId}"); | ||||
|  | ||||
|                 _cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddMinutes(15)); | ||||
|                 _cache.Set($"approvalMembers{subRoleId}", members, DateTimeOffset.Now.AddMinutes(2)); | ||||
|             } else { | ||||
|                 throw new Exception($"Unable to get group members, because {responseMessage.ReasonPhrase}"); | ||||
|             } | ||||
|  | ||||
| @ -1,10 +1,9 @@ | ||||
| using System.IdentityModel.Tokens.Jwt; | ||||
| using System.Security.Claims; | ||||
| using System.Security.Principal; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
|  | ||||
| using Blazored.SessionStorage; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
|  | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
| @ -13,6 +12,7 @@ namespace MesaFabApproval.Client.Services; | ||||
|  | ||||
| public interface IAuthenticationService { | ||||
|     Task<ClaimsPrincipal> SendAuthenticationRequest(string loginId,  string password); | ||||
|     Task<ClaimsPrincipal> AttemptLocalUserAuth(); | ||||
|     Task<ClaimsPrincipal> FetchAuthState(); | ||||
|     Task ClearTokens(); | ||||
|     Task ClearCurrentUser(); | ||||
| @ -26,15 +26,15 @@ public interface IAuthenticationService { | ||||
| } | ||||
|  | ||||
| public class AuthenticationService : IAuthenticationService { | ||||
|     private readonly ISessionStorageService _sessionStorageService; | ||||
|     private readonly ILocalStorageService _localStorageService; | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly IHttpClientFactory _httpClientFactory; | ||||
|  | ||||
|     public AuthenticationService(ISessionStorageService sessionStorageService, | ||||
|     public AuthenticationService(ILocalStorageService localStorageService, | ||||
|                                  IMemoryCache cache, | ||||
|                                  IHttpClientFactory httpClientFactory) { | ||||
|         _sessionStorageService = sessionStorageService ?? | ||||
|             throw new ArgumentNullException("ISessionStorageService not injected"); | ||||
|         _localStorageService = localStorageService ?? | ||||
|             throw new ArgumentNullException("ILocalStorageService not injected"); | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException("IHttpClientFactory not injected"); | ||||
|     } | ||||
| @ -84,10 +84,55 @@ public class AuthenticationService : IAuthenticationService { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<ClaimsPrincipal> AttemptLocalUserAuth() { | ||||
|         WindowsIdentity? user = WindowsIdentity.GetCurrent(); | ||||
|  | ||||
|         if (user is null) | ||||
|             throw new Exception("no authenticated user found"); | ||||
|  | ||||
|         if (!user.IsAuthenticated) | ||||
|             throw new Exception("you are not authenticated"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/login/localWindows"); | ||||
|  | ||||
|         request.Content = new StringContent(JsonSerializer.Serialize(user), | ||||
|                                             Encoding.UTF8, | ||||
|                                             "application/json"); | ||||
|  | ||||
|         HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request); | ||||
|  | ||||
|         if (httpResponseMessage.IsSuccessStatusCode) { | ||||
|             string responseContent = await httpResponseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|             JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                 PropertyNameCaseInsensitive = true | ||||
|             }; | ||||
|  | ||||
|             LoginResult loginResult = JsonSerializer.Deserialize<LoginResult>(responseContent, jsonSerializerOptions) ?? | ||||
|                 throw new Exception("Unable to parse login result from API response"); | ||||
|  | ||||
|             if (!loginResult.IsAuthenticated) throw new Exception($"User with Login ID {user.Name} not authorized"); | ||||
|  | ||||
|             await SetLoginId(loginResult.User.LoginID); | ||||
|  | ||||
|             await SetTokens(loginResult.AuthTokens.JwtToken, loginResult.AuthTokens.RefreshToken); | ||||
|  | ||||
|             await SetCurrentUser(loginResult.User); | ||||
|  | ||||
|             ClaimsPrincipal principal = GetClaimsPrincipalFromJwt(loginResult.AuthTokens.JwtToken); | ||||
|  | ||||
|             return principal; | ||||
|         } else { | ||||
|             throw new Exception($"Login API request failed for {user.Name}, because {httpResponseMessage.ReasonPhrase}"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<ClaimsPrincipal> FetchAuthState() { | ||||
|         string? jwt = _cache.Get<string>("MesaFabApprovalJwt"); | ||||
|  | ||||
|         if (jwt is null) jwt = await _sessionStorageService.GetItemAsync<string>("MesaFabApprovalJwt"); | ||||
|         if (jwt is null) jwt = await _localStorageService.GetItem("MesaFabApprovalJwt"); | ||||
|  | ||||
|         if (jwt is null) throw new Exception("Unable to find JWT"); | ||||
|  | ||||
| @ -98,9 +143,9 @@ public class AuthenticationService : IAuthenticationService { | ||||
|  | ||||
|     public async Task ClearTokens() { | ||||
|         _cache.Remove("MesaFabApprovalJwt"); | ||||
|         await _sessionStorageService.RemoveItemAsync("MesaFabApprovalJwt"); | ||||
|         await _localStorageService.RemoveItem("MesaFabApprovalJwt"); | ||||
|         _cache.Remove("MesaFabApprovalRefreshToken"); | ||||
|         await _sessionStorageService.RemoveItemAsync("MesaFabApprovalRefreshToken"); | ||||
|         await _localStorageService.RemoveItem("MesaFabApprovalRefreshToken"); | ||||
|     } | ||||
|  | ||||
|     public async Task SetTokens(string jwt, string refreshToken) { | ||||
| @ -108,37 +153,37 @@ public class AuthenticationService : IAuthenticationService { | ||||
|         if (string.IsNullOrWhiteSpace(refreshToken)) throw new ArgumentNullException("Refresh token cannot be null or empty"); | ||||
|  | ||||
|         _cache.Set<string>("MesaFabApprovalJwt", jwt); | ||||
|         await _sessionStorageService.SetItemAsync<string>("MesaFabApprovalJwt", jwt); | ||||
|         await _localStorageService.AddItem("MesaFabApprovalJwt", jwt); | ||||
|         _cache.Set<string>("MesaFabApprovalRefreshToken", refreshToken); | ||||
|         await _sessionStorageService.SetItemAsync<string>("MesaFabApprovalRefreshToken", refreshToken); | ||||
|         await _localStorageService.AddItem("MesaFabApprovalRefreshToken", refreshToken); | ||||
|     } | ||||
|  | ||||
|     public async Task SetLoginId(string loginId) { | ||||
|         if (string.IsNullOrWhiteSpace(loginId)) throw new ArgumentNullException("LoginId cannot be null or empty"); | ||||
|  | ||||
|         _cache.Set<string>("MesaFabApprovalUserId", loginId); | ||||
|         await _sessionStorageService.SetItemAsync<string>("MesaFabApprovalUserId", loginId); | ||||
|         await _localStorageService.AddItem("MesaFabApprovalUserId", loginId); | ||||
|     } | ||||
|  | ||||
|     public async Task SetCurrentUser(User user) { | ||||
|         if (user is null) throw new ArgumentNullException("User cannot be null"); | ||||
|  | ||||
|     public async Task SetCurrentUser(User? user) { | ||||
|         _cache.Set<User>("MesaFabApprovalCurrentUser", user); | ||||
|         await _sessionStorageService.SetItemAsync<User>("MesaFabApprovalCurrentUser", user); | ||||
|         await _localStorageService.AddItem<User>("MesaFabApprovalCurrentUser", user); | ||||
|     } | ||||
|  | ||||
|     public async Task ClearCurrentUser() { | ||||
|         _cache.Remove("MesaFabApprovalCurrentUser"); | ||||
|         await _sessionStorageService.RemoveItemAsync("MesaFabApprovalCurrentUser"); | ||||
|         await _localStorageService.RemoveItem("MesaFabApprovalCurrentUser"); | ||||
|         _cache.Remove("MesaFabApprovalUserId"); | ||||
|         await _sessionStorageService.RemoveItemAsync("MesaFabApprovalUserId"); | ||||
|         await _localStorageService.RemoveItem("MesaFabApprovalUserId"); | ||||
|     } | ||||
|  | ||||
|     public async Task<User> GetCurrentUser() { | ||||
|         User? currentUser = null; | ||||
|  | ||||
|         currentUser = _cache.Get<User>("MesaFabApprovalCurrentUser") ?? | ||||
|             await _sessionStorageService.GetItemAsync<User>("MesaFabApprovalCurrentUser"); | ||||
|         currentUser = _cache.Get<User>("MesaFabApprovalCurrentUser"); | ||||
|  | ||||
|         if (currentUser is null) | ||||
|             currentUser = await _localStorageService.GetItem<User>("MesaFabApprovalCurrentUser"); | ||||
|  | ||||
|         return currentUser; | ||||
|     } | ||||
| @ -147,10 +192,10 @@ public class AuthenticationService : IAuthenticationService { | ||||
|         AuthTokens? authTokens = null; | ||||
|  | ||||
|         string? jwt = _cache.Get<string>("MesaFabApprovalJwt"); | ||||
|         if (jwt is null) jwt = await _sessionStorageService.GetItemAsync<string>("MesaFabApprovalJwt"); | ||||
|         if (jwt is null) jwt = await _localStorageService.GetItem("MesaFabApprovalJwt"); | ||||
|  | ||||
|         string? refreshToken = _cache.Get<string>("MesaFabApprovalRefreshToken"); | ||||
|         if (refreshToken is null) refreshToken = await _sessionStorageService.GetItemAsync<string>("MesaFabApprovalRefreshToken"); | ||||
|         if (refreshToken is null) refreshToken = await _localStorageService.GetItem("MesaFabApprovalRefreshToken"); | ||||
|  | ||||
|         if (!string.IsNullOrWhiteSpace(jwt) && !string.IsNullOrWhiteSpace(refreshToken)) { | ||||
|             authTokens = new() {  | ||||
| @ -164,7 +209,7 @@ public class AuthenticationService : IAuthenticationService { | ||||
|  | ||||
|     public async Task<string> GetLoginId() { | ||||
|         string? loginId = _cache.Get<string>("MesaFabApprovalUserId"); | ||||
|         if (loginId is null) loginId = await _sessionStorageService.GetItemAsync<string>("MesaFabApprovalUserId"); | ||||
|         if (loginId is null) loginId = await _localStorageService.GetItem("MesaFabApprovalUserId"); | ||||
|  | ||||
|         return loginId; | ||||
|     } | ||||
|  | ||||
							
								
								
									
										44
									
								
								MesaFabApproval.Client/Services/CAService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								MesaFabApproval.Client/Services/CAService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace MesaFabApproval.Client.Services; | ||||
|  | ||||
| public interface ICAService { | ||||
|     Task<bool> CANumberIsValid(int number); | ||||
| } | ||||
|  | ||||
| public class CAService : ICAService { | ||||
|     private readonly IHttpClientFactory _httpClientFactory; | ||||
|  | ||||
|     public CAService(IHttpClientFactory httpClientFactory) { | ||||
|         _httpClientFactory = httpClientFactory ?? | ||||
|             throw new ArgumentNullException("IHttpClientFactory not injected"); | ||||
|     } | ||||
|  | ||||
|     public async Task<bool> CANumberIsValid(int number) { | ||||
|         if (number <= 0) return false; | ||||
|  | ||||
|         try { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"ca/isValidCANumber?number={number}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 bool isValid = JsonSerializer.Deserialize<bool>(responseContent, jsonSerializerOptions); | ||||
|  | ||||
|                 return isValid; | ||||
|             } else { | ||||
|                 throw new Exception(responseMessage.ReasonPhrase); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             throw new Exception($"Unable to determine if {number} is a valid CA#, because {ex.Message}"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										51
									
								
								MesaFabApproval.Client/Services/CustomerService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								MesaFabApproval.Client/Services/CustomerService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| using System.Text.Json; | ||||
|  | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| namespace MesaFabApproval.Client.Services; | ||||
|  | ||||
| public interface ICustomerService { | ||||
|     Task<IEnumerable<string>> GetAllCustomerNames(); | ||||
| } | ||||
|  | ||||
| public class CustomerService : ICustomerService { | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly IHttpClientFactory _httpClientFactory; | ||||
|  | ||||
|     public CustomerService(IMemoryCache cache, IHttpClientFactory httpClientFactory) { | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _httpClientFactory = httpClientFactory ?? | ||||
|             throw new ArgumentNullException("IHttpClientFactory not injected"); | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<string>> GetAllCustomerNames() { | ||||
|         IEnumerable<string>? allCustomerNames = null; | ||||
|  | ||||
|         allCustomerNames = _cache.Get<IEnumerable<string>>("allCustomerNames"); | ||||
|  | ||||
|         if (allCustomerNames is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"customer/all"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 allCustomerNames = JsonSerializer.Deserialize<IEnumerable<string>>(responseContent, jsonSerializerOptions) ?? | ||||
|                     throw new Exception("Unable to parse names from API response"); | ||||
|  | ||||
|                 _cache.Set($"allCustomerNames", allCustomerNames, DateTimeOffset.Now.AddHours(1)); | ||||
|             } else { | ||||
|                 throw new Exception($"Unable to get all customer names, because {responseMessage.ReasonPhrase}"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return allCustomerNames; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										52
									
								
								MesaFabApproval.Client/Services/ECNService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								MesaFabApproval.Client/Services/ECNService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace MesaFabApproval.Client.Services; | ||||
|  | ||||
| public interface IECNService { | ||||
|     Task<string> ECNNumberIsValidStr(int ecnNumber); | ||||
|     Task<bool> ECNNumberIsValid(int number); | ||||
| } | ||||
|  | ||||
| public class ECNService : IECNService { | ||||
|     private readonly IHttpClientFactory _httpClientFactory; | ||||
|  | ||||
|     public ECNService(IHttpClientFactory httpClientFactory) { | ||||
|         _httpClientFactory = httpClientFactory ?? | ||||
|             throw new ArgumentNullException("IHttpClientFactory not injected"); | ||||
|     } | ||||
|  | ||||
|     public async Task<string> ECNNumberIsValidStr(int ecnNumber) { | ||||
|         if (ecnNumber <= 0 || !await ECNNumberIsValid(ecnNumber)) | ||||
|             return $"{ecnNumber} is not a valid ECN#"; | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public async Task<bool> ECNNumberIsValid(int number) { | ||||
|         if (number <= 0) return false; | ||||
|  | ||||
|         try { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"ecn/isValidEcnNumber?number={number}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 bool isValid = JsonSerializer.Deserialize<bool>(responseContent, jsonSerializerOptions); | ||||
|  | ||||
|                 return isValid; | ||||
|             } else { | ||||
|                 throw new Exception(responseMessage.ReasonPhrase); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             throw new Exception($"Unable to determine if {number} is a valid ECN#, because {ex.Message}"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										49
									
								
								MesaFabApproval.Client/Services/LocalStorageService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								MesaFabApproval.Client/Services/LocalStorageService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| using Microsoft.JSInterop; | ||||
|  | ||||
| namespace MesaFabApproval.Client.Services; | ||||
|  | ||||
| public interface ILocalStorageService { | ||||
|     Task AddItem(string key, string value); | ||||
|     Task AddItem<T>(string key, T value); | ||||
|     Task RemoveItem(string key); | ||||
|     Task<string> GetItem(string key); | ||||
|     Task<T?> GetItem<T>(string key); | ||||
| } | ||||
|  | ||||
| public class LocalStorageService : ILocalStorageService { | ||||
|     private readonly IJSRuntime _jsRuntime; | ||||
|  | ||||
|     public LocalStorageService(IJSRuntime jsRuntime) { | ||||
|         _jsRuntime = jsRuntime; | ||||
|     } | ||||
|  | ||||
|     public async Task AddItem(string key, string value) { | ||||
|         await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, value); | ||||
|     } | ||||
|  | ||||
|     public async Task AddItem<T>(string key, T value) { | ||||
|         string item = JsonSerializer.Serialize(value); | ||||
|         await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, item); | ||||
|     } | ||||
|  | ||||
|     public async Task RemoveItem(string key) { | ||||
|         await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", key); | ||||
|     } | ||||
|  | ||||
|     public async Task<string> GetItem(string key) { | ||||
|         return await _jsRuntime.InvokeAsync<string>("localStorage.getItem", key); | ||||
|     } | ||||
|  | ||||
|     public async Task<T?> GetItem<T>(string key) { | ||||
|         string item = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", key); | ||||
|  | ||||
|         JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|             PropertyNameCaseInsensitive = true | ||||
|         }; | ||||
|  | ||||
|         return JsonSerializer.Deserialize<T>(item, jsonSerializerOptions); | ||||
|     } | ||||
| } | ||||
| @ -3,6 +3,7 @@ using System.Text; | ||||
| using System.Text.Json; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Utilities; | ||||
|  | ||||
| using Microsoft.AspNetCore.Components.Forms; | ||||
| using Microsoft.AspNetCore.StaticFiles; | ||||
| @ -13,10 +14,12 @@ using MudBlazor; | ||||
| namespace MesaFabApproval.Client.Services; | ||||
|  | ||||
| public interface IMRBService { | ||||
|     Task<IEnumerable<MRB>> GetAllMRBs(); | ||||
|     Task<MRB> GetMRBById(int id); | ||||
|     Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache); | ||||
|     Task<MRB> GetMRBById(int id, bool bypassCache = false); | ||||
|     Task<MRB> GetMRBByTitle(string title, bool bypassCache); | ||||
|     Task CreateNewMRB(MRB mrb); | ||||
|     Task RecallMRB(MRB mrb, User recallUser); | ||||
|     Task DeleteMRB(int mrbNumber); | ||||
|     Task UpdateMRB(MRB mrb); | ||||
|     Task SubmitForApproval(MRB mrb); | ||||
|     Task GenerateActionTasks(MRB mrb, MRBAction action); | ||||
| @ -25,11 +28,15 @@ public interface IMRBService { | ||||
|     Task UpdateMRBAction(MRBAction mrbAction); | ||||
|     Task DeleteMRBAction(MRBAction mrbAction); | ||||
|     Task UploadAttachments(IEnumerable<IBrowserFile> files, int mrbNumber); | ||||
|     Task UploadActionAttachments(IEnumerable<IBrowserFile> files, int actionId); | ||||
|     Task<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache); | ||||
|     Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache); | ||||
|     Task DeleteAttachment(MRBAttachment attachment); | ||||
|     Task NotifyNewApprovals(MRB mrb); | ||||
|     Task NotifyApprovers(MRBNotification notification); | ||||
|     Task NotifyOriginator(MRBNotification notification); | ||||
|     Task NotifyQAPreApprover(MRBNotification notification); | ||||
|     Task<bool> NumberIsValid(int number); | ||||
| } | ||||
|  | ||||
| public class MRBService : IMRBService { | ||||
| @ -81,14 +88,63 @@ public class MRBService : IMRBService { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<MRB>> GetAllMRBs() { | ||||
|     public async Task RecallMRB(MRB mrb, User recallUser) { | ||||
|         if (mrb is null) throw new ArgumentNullException("MRB cannot be null"); | ||||
|         if (mrb.StageNo < 1) throw new ArgumentException("MRB already in Draft stage"); | ||||
|         if (mrb.StageNo >= 4) throw new Exception("you cannot recall a completed MRB"); | ||||
|  | ||||
|         mrb.StageNo = 0; | ||||
|         mrb.SubmittedDate = DateTimeUtilities.MIN_DT; | ||||
|         mrb.ApprovalDate = DateTimeUtilities.MAX_DT; | ||||
|         mrb.CloseDate = DateTimeUtilities.MAX_DT; | ||||
|         await UpdateMRB(mrb); | ||||
|  | ||||
|         IEnumerable<Approval> approvals = await _approvalService.GetApprovalsForIssueId(mrb.MRBNumber, false); | ||||
|  | ||||
|         foreach (Approval approval in approvals) { | ||||
|             if (approval.CompletedDate >= DateTimeUtilities.MAX_DT) { | ||||
|                 string comment = $"Recalled by {recallUser.GetFullName()}."; | ||||
|                 approval.Comments = comment; | ||||
|  | ||||
|                 approval.CompletedDate = DateTime.Now; | ||||
|                 approval.ItemStatus = -1; | ||||
|  | ||||
|                 await _approvalService.UpdateApproval(approval); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         string message = $"MRB# {mrb.MRBNumber} [{mrb.Title}] has been recalled by {recallUser.GetFullName()}."; | ||||
|         MRBNotification notification = new() { Message = message, MRB = mrb }; | ||||
|  | ||||
|         await NotifyApprovers(notification); | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteMRB(int mrbNumber) { | ||||
|         if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"mrb/delete?mrbNumber={mrbNumber}"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) { | ||||
|             throw new Exception($"Unable to delete MRB# {mrbNumber}, because {responseMessage.ReasonPhrase}"); | ||||
|         } | ||||
|  | ||||
|         IEnumerable<MRB> allMRBs = await GetAllMRBs(true); | ||||
|         _cache.Set("allMrbs", allMRBs); | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<MRB>> GetAllMRBs(bool bypassCache) { | ||||
|         try { | ||||
|             IEnumerable<MRB>? allMRBs = _cache.Get<IEnumerable<MRB>>("allMrbs"); | ||||
|             IEnumerable<MRB>? allMRBs = null; | ||||
|             if (!bypassCache) allMRBs = _cache.Get<IEnumerable<MRB>>("allMrbs"); | ||||
|  | ||||
|             if (allMRBs is null) { | ||||
|                 HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|                 HttpRequestMessage requestMessage = new(HttpMethod.Get, "mrb/all"); | ||||
|                 HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/all?bypassCache={bypassCache}"); | ||||
|  | ||||
|                 HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
| @ -113,17 +169,48 @@ public class MRBService : IMRBService { | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|     public async Task<MRB> GetMRBById(int id) { | ||||
|  | ||||
|     public async Task<bool> NumberIsValid(int number) { | ||||
|         try { | ||||
|             if (number <= 0) return false; | ||||
|  | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/numberIsValid?number={number}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 bool isValid = JsonSerializer.Deserialize<bool>(responseContent, jsonSerializerOptions); | ||||
|  | ||||
|                 return isValid; | ||||
|             } else { | ||||
|                 throw new Exception(responseMessage.ReasonPhrase); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             throw new Exception($"Unable to determine if {number} is a valid MRB#, because {ex.Message}"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<MRB> GetMRBById(int id, bool bypassCache=false) { | ||||
|         if (id <= 0) throw new ArgumentException($"Invalid MRB number: {id}"); | ||||
|  | ||||
|         MRB? mrb = _cache.Get<MRB>($"mrb{id}"); | ||||
|         MRB? mrb = null; | ||||
|         if (!bypassCache) | ||||
|             mrb = _cache.Get<MRB>($"mrb{id}"); | ||||
|  | ||||
|         if (mrb is null) mrb = _cache.Get<IEnumerable<MRB>>("allMrbs")?.FirstOrDefault(m => m.MRBNumber == id); | ||||
|         if (mrb is null && !bypassCache) mrb = _cache.Get<IEnumerable<MRB>>("allMrbs")?.FirstOrDefault(m => m.MRBNumber == id); | ||||
|  | ||||
|         if (mrb is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/getById?id={id}"); | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/getById?id={id}&bypassCache={bypassCache}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
| @ -147,12 +234,13 @@ public class MRBService : IMRBService { | ||||
|     } | ||||
|  | ||||
|     public async Task<MRB> GetMRBByTitle(string title, bool bypassCache) { | ||||
|         if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or emtpy"); | ||||
|         if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Title cannot be null or empty"); | ||||
|  | ||||
|         MRB? mrb = null; | ||||
|         if (!bypassCache) mrb = _cache.Get<MRB>($"mrb{title}"); | ||||
|  | ||||
|         if (mrb is null) mrb = _cache.Get<IEnumerable<MRB>>("allMrbs")?.FirstOrDefault(m => m.Title.Equals(title)); | ||||
|         if (mrb is null && !bypassCache)  | ||||
|             mrb = _cache.Get<IEnumerable<MRB>>("allMrbs")?.FirstOrDefault(m => m.Title.Equals(title)); | ||||
|  | ||||
|         if (mrb is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
| @ -324,7 +412,8 @@ public class MRBService : IMRBService { | ||||
|  | ||||
|         foreach (IBrowserFile file in files) { | ||||
|             try { | ||||
|                 StreamContent fileContent = new StreamContent(file.OpenReadStream()); | ||||
|                 long maxFileSize = 1024L * 1024L * 1024L * 2L; | ||||
|                 StreamContent fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); | ||||
|  | ||||
|                 FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider(); | ||||
|  | ||||
| @ -349,9 +438,85 @@ public class MRBService : IMRBService { | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception($"Unable to save attachments, because {responseMessage.ReasonPhrase}"); | ||||
|  | ||||
|         string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|         JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|             PropertyNameCaseInsensitive = true | ||||
|         }; | ||||
|  | ||||
|         IEnumerable<UploadResult> results = JsonSerializer.Deserialize<IEnumerable<UploadResult>>(responseContent, jsonSerializerOptions) ?? | ||||
|             new List<UploadResult>(); | ||||
|  | ||||
|         foreach (UploadResult result in results) { | ||||
|             if (result.UploadSuccessful) { | ||||
|                 _snackbar.Add($"{result.FileName} successfully uploaded", Severity.Success); | ||||
|             } else { | ||||
|                 _snackbar.Add($"{result.FileName} not uploaded, because {result.Error}", Severity.Error); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         await GetAllAttachmentsForMRB(mrbNumber, true); | ||||
|     } | ||||
|  | ||||
|     public async Task UploadActionAttachments(IEnumerable<IBrowserFile> files, int actionId) { | ||||
|         if (files is null) throw new ArgumentNullException("Files cannot be null"); | ||||
|         if (files.Count() <= 0) throw new ArgumentException("Files cannot be empty"); | ||||
|         if (actionId <= 0) throw new ArgumentException($"{actionId} is not a valid MRB action ID"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"mrb/action/attach?actionId={actionId}"); | ||||
|  | ||||
|         using MultipartFormDataContent content = new MultipartFormDataContent(); | ||||
|  | ||||
|         foreach (IBrowserFile file in files) { | ||||
|             try { | ||||
|                 long maxFileSize = 1024L * 1024L * 1024L * 2L; | ||||
|                 StreamContent fileContent = new StreamContent(file.OpenReadStream(maxFileSize)); | ||||
|  | ||||
|                 FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider(); | ||||
|  | ||||
|                 const string defaultContentType = "application/octet-stream"; | ||||
|  | ||||
|                 if (!contentTypeProvider.TryGetContentType(file.Name, out string? contentType)) { | ||||
|                     contentType = defaultContentType; | ||||
|                 } | ||||
|  | ||||
|                 fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType); | ||||
|  | ||||
|                 content.Add(content: fileContent, name: "\"files\"", fileName: file.Name); | ||||
|             } catch (Exception ex) { | ||||
|                 _snackbar.Add($"File {file.Name} not saved, because {ex.Message}"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         requestMessage.Content = content; | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception($"Unable to save action attachments, because {responseMessage.ReasonPhrase}"); | ||||
|  | ||||
|         string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|         JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|             PropertyNameCaseInsensitive = true | ||||
|         }; | ||||
|  | ||||
|         IEnumerable<UploadResult> results = JsonSerializer.Deserialize<IEnumerable<UploadResult>>(responseContent, jsonSerializerOptions) ?? | ||||
|             new List<UploadResult>(); | ||||
|  | ||||
|         foreach (UploadResult result in results) { | ||||
|             if (result.UploadSuccessful) { | ||||
|                 _snackbar.Add($"{result.FileName} successfully uploaded", Severity.Success); | ||||
|             } else { | ||||
|                 _snackbar.Add($"{result.FileName} not uploaded, because {result.Error}", Severity.Error); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         await GetAllAttachmentsForMRB(actionId, true); | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<MRBAttachment>> GetAllAttachmentsForMRB(int mrbNumber, bool bypassCache) { | ||||
|         if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#"); | ||||
|  | ||||
| @ -386,6 +551,40 @@ public class MRBService : IMRBService { | ||||
|         return mrbAttachments; | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<MRBActionAttachment>> GetAllActionAttachmentsForMRB(int mrbNumber, bool bypassCache) { | ||||
|         if (mrbNumber <= 0) throw new ArgumentException($"{mrbNumber} is not a valid MRB#"); | ||||
|  | ||||
|         IEnumerable<MRBActionAttachment>? actionAttachments = null; | ||||
|         if (!bypassCache) | ||||
|             actionAttachments = _cache.Get<IEnumerable<MRBActionAttachment>>($"mrbActionAttachments{mrbNumber}"); | ||||
|  | ||||
|         if (actionAttachments is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"mrb/action/attachments?mrbNumber={mrbNumber}&bypassCache={bypassCache}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 actionAttachments = JsonSerializer.Deserialize<IEnumerable<MRBActionAttachment>>(responseContent, jsonSerializerOptions) ?? | ||||
|                     new List<MRBActionAttachment>(); | ||||
|  | ||||
|                 if (actionAttachments.Count() > 0) | ||||
|                     _cache.Set($"mrbActionAttachments{mrbNumber}", actionAttachments, DateTimeOffset.Now.AddMinutes(5)); | ||||
|             } else { | ||||
|                 throw new Exception($"Unable to get MRB {mrbNumber} action attachments, because {responseMessage.ReasonPhrase}"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return actionAttachments; | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteAttachment(MRBAttachment attachment) { | ||||
|         if (attachment is null) throw new ArgumentNullException("MRB attachment cannot be null"); | ||||
|  | ||||
| @ -534,4 +733,23 @@ public class MRBService : IMRBService { | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception($"Unable to notify MRB originator, because {responseMessage.ReasonPhrase}"); | ||||
|     } | ||||
|  | ||||
|     public async Task NotifyQAPreApprover(MRBNotification notification) { | ||||
|         if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||
|         if (notification.MRB is null) throw new ArgumentNullException("MRB cannot be null"); | ||||
|         if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"mrb/notify/qa-pre-approver"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification), | ||||
|                                                     Encoding.UTF8, | ||||
|                                                     "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception($"Unable to notify QA pre approver, because {responseMessage.ReasonPhrase}"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -46,6 +46,16 @@ public class MesaFabApprovalAuthStateProvider : AuthenticationStateProvider, IDi | ||||
|         CurrentUser = await _authService.GetCurrentUser(); | ||||
|  | ||||
|         NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal))); | ||||
|  | ||||
|         if (CurrentUser is null) { | ||||
|             string loginId = _userService.GetLoginIdFromClaimsPrincipal(principal); | ||||
|  | ||||
|             User? user = await _userService.GetUserByLoginId(loginId); | ||||
|  | ||||
|             await _authService.SetCurrentUser(user); | ||||
|  | ||||
|             NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal))); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task LoginAsync(string loginId, string password) { | ||||
| @ -64,6 +74,19 @@ public class MesaFabApprovalAuthStateProvider : AuthenticationStateProvider, IDi | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task LoginLocal() { | ||||
|         try { | ||||
|             ClaimsPrincipal principal = await _authService.AttemptLocalUserAuth(); | ||||
|  | ||||
|             CurrentUser = await _authService.GetCurrentUser(); | ||||
|  | ||||
|             NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal))); | ||||
|         } catch (Exception ex) { | ||||
|             _snackbar.Add(ex.Message, Severity.Error); | ||||
|             NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new()))); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task Logout() { | ||||
|         CurrentUser = null; | ||||
|         await _authService.ClearTokens(); | ||||
| @ -82,7 +105,7 @@ public class MesaFabApprovalAuthStateProvider : AuthenticationStateProvider, IDi | ||||
|                 CurrentUser = await _authService.GetCurrentUser(); | ||||
|             } | ||||
|         } catch (Exception ex) { | ||||
|             Console.WriteLine($"Unable to get authentication state, because {ex.Message}"); | ||||
|             // _snackbar.Add($"Unable to fetch authentication state, because {ex.Message}", Severity.Error); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										767
									
								
								MesaFabApproval.Client/Services/PCRBService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										767
									
								
								MesaFabApproval.Client/Services/PCRBService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,767 @@ | ||||
| using System.Net.Http.Headers; | ||||
| using System.Text; | ||||
| using System.Text.Json; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
|  | ||||
| using Microsoft.AspNetCore.Components.Forms; | ||||
| using Microsoft.AspNetCore.StaticFiles; | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| using MudBlazor; | ||||
|  | ||||
| namespace MesaFabApproval.Client.Services; | ||||
|  | ||||
| public interface IPCRBService { | ||||
|     Task<string> IdIsValid(string id); | ||||
|     Task<bool> IdIsValid(int id); | ||||
|     Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache); | ||||
|     Task CreateNewPCRB(PCRB pcrb); | ||||
|     Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task<PCRB> GetPCRBByTitle(string title, bool bypassCache); | ||||
|     Task UpdatePCRB(PCRB pcrb); | ||||
|     Task DeletePCRB(int planNumber); | ||||
|     Task UploadAttachment(PCRBAttachment attachment); | ||||
|     Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task UpdateAttachment(PCRBAttachment attachment); | ||||
|     Task DeleteAttachment(PCRBAttachment attachment); | ||||
|     Task CreateNewActionItem(PCRBActionItem actionItem); | ||||
|     Task UpdateActionItem(PCRBActionItem actionItem); | ||||
|     Task DeleteActionItem(int id); | ||||
|     Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task CreateNewAttendee(PCRBAttendee attendee); | ||||
|     Task UpdateAttendee(PCRBAttendee attendee); | ||||
|     Task DeleteAttendee(int id); | ||||
|     Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task CreatePCR3Document(PCR3Document document); | ||||
|     Task UpdatePCR3Document(PCR3Document document); | ||||
|     Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache); | ||||
|     Task NotifyNewApprovals(PCRB pcrb); | ||||
|     Task NotifyApprovers(PCRBNotification notification); | ||||
|     Task NotifyOriginator(PCRBNotification notification); | ||||
|     Task NotifyResponsiblePerson(PCRBActionItemNotification notification); | ||||
| } | ||||
|  | ||||
| public class PCRBService : IPCRBService { | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly IHttpClientFactory _httpClientFactory; | ||||
|     private readonly ISnackbar _snackbar; | ||||
|     private readonly IUserService _userService; | ||||
|  | ||||
|     public PCRBService(IMemoryCache cache, | ||||
|                        IHttpClientFactory httpClientFactory, | ||||
|                        ISnackbar snackbar, | ||||
|                        IUserService userService) { | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException("IHttpClientFactory not injected"); | ||||
|         _snackbar = snackbar ?? throw new ArgumentNullException("ISnackbar not injected"); | ||||
|         _userService = userService ?? throw new ArgumentNullException("IUserService not injected"); | ||||
|     } | ||||
|  | ||||
|     public async Task<string> IdIsValid(string id) { | ||||
|         bool isMatch = true; | ||||
|         if (string.IsNullOrWhiteSpace(id)) isMatch = false; | ||||
|  | ||||
|         try { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/getByPlanNumber?planNumber={id}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 PCRB? pcrb = JsonSerializer.Deserialize<PCRB>(responseContent, jsonSerializerOptions); | ||||
|  | ||||
|                 if (pcrb is null) isMatch = false; | ||||
|             } else { | ||||
|                 isMatch = false; | ||||
|             } | ||||
|         } catch (Exception) { | ||||
|             isMatch = false; | ||||
|         } | ||||
|  | ||||
|         if (!isMatch) return $"{id} is not a valid PCRB#"; | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public async Task<bool> IdIsValid(int id) { | ||||
|         bool isMatch = true; | ||||
|         if (id <= 0) isMatch = false; | ||||
|  | ||||
|         try { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/getByPlanNumber?planNumber={id}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 PCRB? pcrb = JsonSerializer.Deserialize<PCRB>(responseContent, jsonSerializerOptions); | ||||
|  | ||||
|                 if (pcrb is null) isMatch = false; | ||||
|             } else { | ||||
|                 isMatch = false; | ||||
|             } | ||||
|         } catch (Exception) { | ||||
|             isMatch = false; | ||||
|         } | ||||
|  | ||||
|         return isMatch; | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCRB>> GetAllPCRBs(bool bypassCache) { | ||||
|         try { | ||||
|             IEnumerable<PCRB>? allPCRBs = null; | ||||
|             if (!bypassCache) allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs"); | ||||
|  | ||||
|             if (allPCRBs is null) { | ||||
|                 HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|                 HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/all?bypassCache={bypassCache}"); | ||||
|  | ||||
|                 HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|                 if (responseMessage.IsSuccessStatusCode) { | ||||
|                     string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                     JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                         PropertyNameCaseInsensitive = true | ||||
|                     }; | ||||
|  | ||||
|                     allPCRBs = JsonSerializer.Deserialize<IEnumerable<PCRB>>(responseContent, jsonSerializerOptions) ?? | ||||
|                         throw new Exception("Unable to parse PCRBs from API response"); | ||||
|  | ||||
|                     _cache.Set($"allPCRBs", allPCRBs, DateTimeOffset.Now.AddMinutes(15)); | ||||
|                 } else { | ||||
|                     throw new Exception(responseMessage.ReasonPhrase); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return allPCRBs; | ||||
|         } catch (Exception) { | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task CreateNewPCRB(PCRB pcrb) { | ||||
|         if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(pcrb), | ||||
|                                                Encoding.UTF8, | ||||
|                                                "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|  | ||||
|         PCRB newPCRB = await GetPCRBByTitle(pcrb.Title, true); | ||||
|  | ||||
|         _cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|         _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|  | ||||
|         IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs"); | ||||
|         if (allPCRBs is not null) { | ||||
|             List<PCRB> pcrbList = allPCRBs.ToList(); | ||||
|             pcrbList.Add(newPCRB); | ||||
|             _cache.Set("allPCRBs", pcrbList); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task<PCRB> GetPCRBByPlanNumber(int planNumber, bool bypassCache) { | ||||
|         if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan #"); | ||||
|  | ||||
|         PCRB? pcrb = null; | ||||
|         if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{planNumber}"); | ||||
|  | ||||
|         if (pcrb is null && !bypassCache) | ||||
|             pcrb = _cache.Get<IEnumerable<PCRB>>("allPCRBs")?.FirstOrDefault(m => m.PlanNumber == planNumber); | ||||
|  | ||||
|         if (pcrb is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = | ||||
|                 new(HttpMethod.Get, $"pcrb/getByPlanNumber?planNumber={planNumber}&bypassCache={bypassCache}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (!responseMessage.IsSuccessStatusCode) | ||||
|                 throw new Exception(responseMessage.ReasonPhrase); | ||||
|  | ||||
|             string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|             JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                 PropertyNameCaseInsensitive = true | ||||
|             }; | ||||
|  | ||||
|             pcrb = JsonSerializer.Deserialize<PCRB>(responseContent, jsonSerializerOptions) ?? | ||||
|                 throw new Exception("unable to parse PCRB from API response"); | ||||
|  | ||||
|             _cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|             _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|  | ||||
|             if (bypassCache) { | ||||
|                 IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs"); | ||||
|                 if (allPCRBs is not null) { | ||||
|                     List<PCRB> pcrbList = allPCRBs.ToList(); | ||||
|                     pcrbList.RemoveAll(p => p.PlanNumber == planNumber); | ||||
|                     pcrbList.Add(pcrb); | ||||
|                     _cache.Set("allPCRBs", pcrbList); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return pcrb; | ||||
|     } | ||||
|  | ||||
|     public async Task<PCRB> GetPCRBByTitle(string title, bool bypassCache) { | ||||
|         if (string.IsNullOrWhiteSpace(title)) throw new ArgumentNullException("title cannot be null"); | ||||
|  | ||||
|         PCRB? pcrb = null; | ||||
|         if (!bypassCache) pcrb = _cache.Get<PCRB>($"pcrb{title}"); | ||||
|  | ||||
|         if (pcrb is null && !bypassCache)  | ||||
|             pcrb = _cache.Get<IEnumerable<PCRB>>("allPCRBs")?.FirstOrDefault(m => m.Title.Equals(title)); | ||||
|  | ||||
|         if (pcrb is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage =  | ||||
|                 new(HttpMethod.Get, $"pcrb/getByTitle?title={title}&bypassCache={bypassCache}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (!responseMessage.IsSuccessStatusCode) | ||||
|                 throw new Exception(responseMessage.ReasonPhrase); | ||||
|  | ||||
|             string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|             JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                 PropertyNameCaseInsensitive = true | ||||
|             }; | ||||
|  | ||||
|             pcrb = JsonSerializer.Deserialize<PCRB>(responseContent, jsonSerializerOptions) ?? | ||||
|                 throw new Exception("unable to parse PCRB from API response"); | ||||
|  | ||||
|             _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|             _cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|  | ||||
|             if (bypassCache) { | ||||
|                 IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs"); | ||||
|                 if (allPCRBs is not null) { | ||||
|                     List<PCRB> pcrbList = allPCRBs.ToList(); | ||||
|                     pcrbList.RemoveAll(p => p.PlanNumber == pcrb.PlanNumber); | ||||
|                     pcrbList.Add(pcrb); | ||||
|                     _cache.Set("allPCRBs", pcrbList); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return pcrb; | ||||
|     } | ||||
|  | ||||
|     public async Task UpdatePCRB(PCRB pcrb) { | ||||
|         if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(pcrb), | ||||
|                                                    Encoding.UTF8, | ||||
|                                                    "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|  | ||||
|         _cache.Set($"pcrb{pcrb.PlanNumber}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|         _cache.Set($"pcrb{pcrb.Title}", pcrb, DateTimeOffset.Now.AddHours(1)); | ||||
|  | ||||
|         IEnumerable<PCRB>? allPCRBs = _cache.Get<IEnumerable<PCRB>>("allPCRBs"); | ||||
|         if (allPCRBs is not null) { | ||||
|             List<PCRB> pcrbList = allPCRBs.ToList(); | ||||
|             pcrbList.RemoveAll(m => m.PlanNumber == pcrb.PlanNumber); | ||||
|             pcrbList.Add(pcrb); | ||||
|             _cache.Set("allPCRBs", pcrbList); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public async Task DeletePCRB(int planNumber) { | ||||
|         if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB plan #"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb?planNumber={planNumber}"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase); | ||||
|  | ||||
|         IEnumerable<PCRB> allPCRBs = await GetAllPCRBs(true); | ||||
|         _cache.Set("allPCRBs", allPCRBs); | ||||
|     } | ||||
|  | ||||
|     public async Task UploadAttachment(PCRBAttachment attachment) { | ||||
|         if (attachment is null) throw new ArgumentNullException("attachment cannot be null"); | ||||
|         if (attachment.File is null) throw new ArgumentNullException("file cannot be null"); | ||||
|         if (attachment.File.Size <= 0) throw new ArgumentException("file size must be greater than zero"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/attachment"); | ||||
|  | ||||
|         using MultipartFormDataContent content = new MultipartFormDataContent(); | ||||
|  | ||||
|         try { | ||||
|             long maxFileSize = 1024L * 1024L * 1024L * 2L; | ||||
|             StreamContent fileContent = new StreamContent(attachment.File.OpenReadStream(maxFileSize)); | ||||
|  | ||||
|             FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider(); | ||||
|  | ||||
|             const string defaultContentType = "application/octet-stream"; | ||||
|  | ||||
|             if (!contentTypeProvider.TryGetContentType(attachment.File.Name, out string? contentType)) { | ||||
|                 contentType = defaultContentType; | ||||
|             } | ||||
|  | ||||
|             fileContent.Headers.ContentType = new MediaTypeHeaderValue(contentType); | ||||
|  | ||||
|             content.Add(content: fileContent, name: "\"file\"", fileName: attachment.File.Name); | ||||
|         } catch (Exception ex) { | ||||
|             _snackbar.Add(ex.Message); | ||||
|         } | ||||
|  | ||||
|         content.Add(new StringContent(attachment.PlanNumber.ToString()), "PlanNumber"); | ||||
|         content.Add(new StringContent(attachment.FileName), "FileName"); | ||||
|         content.Add(new StringContent(attachment.UploadedByID.ToString()), "UploadedByID"); | ||||
|         content.Add(new StringContent(attachment.Title), "Title"); | ||||
|         content.Add(new StringContent(attachment.UploadDateTime.ToString("yyyy-MM-dd HH:mm:ss")), "UploadDateTime"); | ||||
|         content.Add(new StringContent(attachment.Step.ToString()), "Step"); | ||||
|  | ||||
|         requestMessage.Content = content; | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|  | ||||
|         await GetAttachmentsByPlanNumber(attachment.PlanNumber, true); | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCRBAttachment>> GetAttachmentsByPlanNumber(int planNumber, bool bypassCache) { | ||||
|         if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); | ||||
|  | ||||
|         IEnumerable<PCRBAttachment>? attachments = null; | ||||
|         if (!bypassCache) | ||||
|             attachments = _cache.Get<IEnumerable<PCRBAttachment>>($"pcrbAttachments{planNumber}"); | ||||
|  | ||||
|         if (attachments is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/attachments?planNumber={planNumber}&bypassCache={bypassCache}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 attachments = JsonSerializer.Deserialize<IEnumerable<PCRBAttachment>>(responseContent, jsonSerializerOptions) ?? | ||||
|                     new List<PCRBAttachment>(); | ||||
|  | ||||
|                 if (attachments.Count() > 0) { | ||||
|                     foreach (PCRBAttachment attachment in attachments) { | ||||
|                         attachment.UploadedBy = await _userService.GetUserByUserId(attachment.UploadedByID); | ||||
|                     } | ||||
|  | ||||
|                     _cache.Set($"pcrbAttachments{planNumber}", attachments, DateTimeOffset.Now.AddMinutes(5)); | ||||
|                 } | ||||
|             } else { | ||||
|                 throw new Exception(responseMessage.ReasonPhrase); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return attachments; | ||||
|     } | ||||
|  | ||||
|     public async Task UpdateAttachment(PCRBAttachment attachment) { | ||||
|         if (attachment is null) throw new ArgumentNullException("attachment cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/attachment"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(attachment), | ||||
|                                                    Encoding.UTF8, | ||||
|                                                    "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteAttachment(PCRBAttachment attachment) { | ||||
|         if (attachment is null) throw new ArgumentNullException("attachment cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/attachment"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(attachment), | ||||
|                                                    Encoding.UTF8, | ||||
|                                                    "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|     } | ||||
|  | ||||
|     public async Task CreateNewActionItem(PCRBActionItem actionItem) { | ||||
|         if (actionItem is null) throw new ArgumentNullException("action item cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/actionItem"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(actionItem), | ||||
|                                                Encoding.UTF8, | ||||
|                                                "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|  | ||||
|         await GetActionItemsForPlanNumber(actionItem.PlanNumber, true); | ||||
|     } | ||||
|  | ||||
|     public async Task UpdateActionItem(PCRBActionItem actionItem) { | ||||
|         if (actionItem is null) throw new ArgumentNullException("action item cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/actionItem"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(actionItem), | ||||
|                                                    Encoding.UTF8, | ||||
|                                                    "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteActionItem(int id) { | ||||
|         if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB action item ID"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/actionItem?id={id}"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase); | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCRBActionItem>> GetActionItemsForPlanNumber(int planNumber, bool bypassCache) { | ||||
|         if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); | ||||
|  | ||||
|         IEnumerable<PCRBActionItem>? actionItems = null; | ||||
|         if (!bypassCache) | ||||
|             actionItems = _cache.Get<IEnumerable<PCRBActionItem>>($"pcrbActionItems{planNumber}"); | ||||
|  | ||||
|         if (actionItems is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/actionItems?planNumber={planNumber}&bypassCache={bypassCache}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 actionItems = JsonSerializer.Deserialize<IEnumerable<PCRBActionItem>>(responseContent, jsonSerializerOptions) ?? | ||||
|                     new List<PCRBActionItem>(); | ||||
|  | ||||
|                 if (actionItems.Count() > 0) { | ||||
|                     foreach (PCRBActionItem actionItem in actionItems) { | ||||
|                         actionItem.UploadedBy = await _userService.GetUserByUserId(actionItem.UploadedByID); | ||||
|                         if (actionItem.ResponsiblePersonID > 0) | ||||
|                             actionItem.ResponsiblePerson = await _userService.GetUserByUserId(actionItem.ResponsiblePersonID); | ||||
|                         if (actionItem.ClosedByID > 0) | ||||
|                             actionItem.ClosedBy = await _userService.GetUserByUserId(actionItem.ClosedByID); | ||||
|                     } | ||||
|  | ||||
|                     _cache.Set($"pcrbActionItems{planNumber}", actionItems, DateTimeOffset.Now.AddMinutes(5)); | ||||
|                 } | ||||
|             } else { | ||||
|                 throw new Exception(responseMessage.ReasonPhrase); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return actionItems; | ||||
|     } | ||||
|  | ||||
|     public async Task CreateNewAttendee(PCRBAttendee attendee) { | ||||
|         if (attendee is null) throw new ArgumentNullException("attendee cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/attendee"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(attendee), | ||||
|                                                Encoding.UTF8, | ||||
|                                                "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|  | ||||
|         await GetAttendeesByPlanNumber(attendee.PlanNumber, true); | ||||
|     } | ||||
|  | ||||
|     public async Task UpdateAttendee(PCRBAttendee attendee) { | ||||
|         if (attendee is null) throw new ArgumentNullException("attendee cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/attendee"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(attendee), | ||||
|                                                    Encoding.UTF8, | ||||
|                                                    "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|     } | ||||
|  | ||||
|     public async Task DeleteAttendee(int id) { | ||||
|         if (id <= 0) throw new ArgumentException($"{id} is not a valid PCRB attendee ID"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Delete, $"pcrb/attendee?id={id}"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) throw new Exception(responseMessage.ReasonPhrase); | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCRBAttendee>> GetAttendeesByPlanNumber(int planNumber, bool bypassCache) { | ||||
|         if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); | ||||
|  | ||||
|         IEnumerable<PCRBAttendee>? attendees = null; | ||||
|         if (!bypassCache) | ||||
|             attendees = _cache.Get<IEnumerable<PCRBAttendee>>($"pcrbAttendees{planNumber}"); | ||||
|  | ||||
|         if (attendees is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/attendees?planNumber={planNumber}&bypassCache={bypassCache}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 attendees = JsonSerializer.Deserialize<IEnumerable<PCRBAttendee>>(responseContent, jsonSerializerOptions) ?? | ||||
|                     new List<PCRBAttendee>(); | ||||
|  | ||||
|                 if (attendees.Count() > 0) { | ||||
|                     foreach (PCRBAttendee attendee in attendees) | ||||
|                         attendee.Attendee = await _userService.GetUserByUserId(attendee.AttendeeID); | ||||
|  | ||||
|                     _cache.Set($"pcrbAttendees{planNumber}", attendees, DateTimeOffset.Now.AddMinutes(5)); | ||||
|                 } | ||||
|             } else { | ||||
|                 throw new Exception(responseMessage.ReasonPhrase); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return attendees; | ||||
|     } | ||||
|  | ||||
|     public async Task CreatePCR3Document(PCR3Document document) { | ||||
|         if (document is null) throw new ArgumentNullException("document cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/pcr3Document"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(document), | ||||
|                                                Encoding.UTF8, | ||||
|                                                "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|  | ||||
|         await GetPCR3DocumentsForPlanNumber(document.PlanNumber, true); | ||||
|     } | ||||
|  | ||||
|     public async Task UpdatePCR3Document(PCR3Document document) { | ||||
|         if (document is null) throw new ArgumentNullException("document cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Put, $"pcrb/pcr3Document"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(document), | ||||
|                                                    Encoding.UTF8, | ||||
|                                                    "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception(responseMessage.ReasonPhrase); | ||||
|     } | ||||
|  | ||||
|     public async Task<IEnumerable<PCR3Document>> GetPCR3DocumentsForPlanNumber(int planNumber, bool bypassCache) { | ||||
|         if (planNumber <= 0) throw new ArgumentException($"{planNumber} is not a valid PCRB Plan#"); | ||||
|  | ||||
|         IEnumerable<PCR3Document>? documents = null; | ||||
|         if (!bypassCache) | ||||
|             documents = _cache.Get<IEnumerable<PCR3Document>>($"pcr3Documents{planNumber}"); | ||||
|  | ||||
|         if (documents is null) { | ||||
|             HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|             HttpRequestMessage requestMessage = new(HttpMethod.Get, $"pcrb/pcr3Documents?planNumber={planNumber}&bypassCache={bypassCache}"); | ||||
|  | ||||
|             HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|             if (responseMessage.IsSuccessStatusCode) { | ||||
|                 string responseContent = await responseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                 JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                     PropertyNameCaseInsensitive = true | ||||
|                 }; | ||||
|  | ||||
|                 documents = JsonSerializer.Deserialize<IEnumerable<PCR3Document>>(responseContent, jsonSerializerOptions) ?? | ||||
|                     new List<PCR3Document>(); | ||||
|  | ||||
|                 if (documents.Count() > 0) { | ||||
|                     foreach (PCR3Document document in documents) { | ||||
|                         if (document.CompletedByID > 0) | ||||
|                             document.CompletedBy = await _userService.GetUserByUserId(document.CompletedByID); | ||||
|                     } | ||||
|  | ||||
|                     _cache.Set($"pcr3Documents{planNumber}", documents, DateTimeOffset.Now.AddMinutes(5)); | ||||
|                 } | ||||
|             } else { | ||||
|                 throw new Exception(responseMessage.ReasonPhrase); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return documents; | ||||
|     } | ||||
|  | ||||
|     public async Task NotifyNewApprovals(PCRB pcrb) { | ||||
|         if (pcrb is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/new-approvals"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(pcrb), | ||||
|                                                     Encoding.UTF8, | ||||
|                                                     "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception($"Unable to notify new PCRB approvers, because {responseMessage.ReasonPhrase}"); | ||||
|     } | ||||
|  | ||||
|     public async Task NotifyApprovers(PCRBNotification notification) { | ||||
|         if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||
|         if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|         if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/approvers"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification), | ||||
|                                                     Encoding.UTF8, | ||||
|                                                     "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception($"Unable to notify PCRB approvers, because {responseMessage.ReasonPhrase}"); | ||||
|     } | ||||
|  | ||||
|     public async Task NotifyOriginator(PCRBNotification notification) { | ||||
|         if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||
|         if (notification.PCRB is null) throw new ArgumentNullException("PCRB cannot be null"); | ||||
|         if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/originator"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification), | ||||
|                                                     Encoding.UTF8, | ||||
|                                                     "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception($"Unable to notify PCRB originator, because {responseMessage.ReasonPhrase}"); | ||||
|     } | ||||
|  | ||||
|     public async Task NotifyResponsiblePerson(PCRBActionItemNotification notification) { | ||||
|         if (notification is null) throw new ArgumentNullException("notification cannot be null"); | ||||
|         if (string.IsNullOrWhiteSpace(notification.Message)) throw new ArgumentException("message cannot be null or empty"); | ||||
|  | ||||
|         HttpClient httpClient = _httpClientFactory.CreateClient("API"); | ||||
|  | ||||
|         HttpRequestMessage requestMessage = new(HttpMethod.Post, $"pcrb/notify/responsiblePerson"); | ||||
|  | ||||
|         requestMessage.Content = new StringContent(JsonSerializer.Serialize(notification), | ||||
|                                                     Encoding.UTF8, | ||||
|                                                     "application/json"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await httpClient.SendAsync(requestMessage); | ||||
|  | ||||
|         if (!responseMessage.IsSuccessStatusCode) | ||||
|             throw new Exception($"Unable to notify PCRB responsible person, because {responseMessage.ReasonPhrase}"); | ||||
|     } | ||||
| } | ||||
| @ -3,6 +3,7 @@ using System.Text.Json; | ||||
|  | ||||
| using MesaFabApproval.Shared.Models; | ||||
|  | ||||
| using Microsoft.AspNetCore.Components.WebAssembly.Http; | ||||
| using Microsoft.Extensions.Caching.Memory; | ||||
|  | ||||
| namespace MesaFabApproval.Client.Services; | ||||
| @ -141,7 +142,7 @@ public class UserService : IUserService { | ||||
|         } | ||||
|  | ||||
|         if (activeUsers is null) | ||||
|             activeUsers = new List<User>(); | ||||
|             throw new Exception("unable to fetch all active users"); | ||||
|  | ||||
|         return activeUsers; | ||||
|     } | ||||
|  | ||||
| @ -1,11 +0,0 @@ | ||||
| namespace MesaFabApproval.Client.Util; | ||||
|  | ||||
| public class DateTimeUtilities { | ||||
|     public static string GetDateAsStringMinDefault(DateTime dt) { | ||||
|         return dt > DateTime.MinValue ? dt.ToString("yyyy-MM-dd HH:mm") : ""; | ||||
|     } | ||||
|  | ||||
|     public static string GetDateAsStringMaxDefault(DateTime dt) { | ||||
|         return dt < DateTime.MaxValue ? dt.ToString("yyyy-MM-dd HH:mm") : ""; | ||||
|     } | ||||
| } | ||||
| @ -8,12 +8,16 @@ using Microsoft.Extensions.Caching.Memory; | ||||
| using System.Net.Http.Headers; | ||||
| using MesaFabApproval.Client.Services; | ||||
| using Microsoft.AspNetCore.Components; | ||||
| using MudBlazor; | ||||
| using System.Net.Http; | ||||
|  | ||||
| namespace MesaFabApproval.Client.Utilities; | ||||
|  | ||||
| public class ApiHttpClientHandler : DelegatingHandler { | ||||
|     private readonly IMemoryCache _cache; | ||||
|     private readonly IAuthenticationService _authService; | ||||
|     private readonly IHttpClientFactory _httpClientFactory; | ||||
|     private readonly ISnackbar _snackbar; | ||||
|     private readonly MesaFabApprovalAuthStateProvider _authStateProvider; | ||||
|     private readonly NavigationManager _navigationManager; | ||||
|  | ||||
| @ -22,11 +26,16 @@ public class ApiHttpClientHandler : DelegatingHandler { | ||||
|     public ApiHttpClientHandler(IMemoryCache cache, | ||||
|                                 IConfiguration config, | ||||
|                                 IAuthenticationService authService, | ||||
|                                 IHttpClientFactory httpClientFactory, | ||||
|                                 ISnackbar snackbar, | ||||
|                                 MesaFabApprovalAuthStateProvider authStateProvider, | ||||
|                                 NavigationManager navigationManager) { | ||||
|         _cache = cache ?? throw new ArgumentNullException("IMemoryCache not injected"); | ||||
|         _apiBaseUrl = config["FabApprovalApiBaseUrl"] ?? | ||||
|             throw new NullReferenceException("FabApprovalApiBaseUrl not found in config"); | ||||
|         _httpClientFactory = httpClientFactory ?? | ||||
|             throw new ArgumentNullException("IHttpClientFactory not injected"); | ||||
|         _snackbar = snackbar ?? throw new ArgumentNullException("ISnackbar not injected"); | ||||
|         _authService = authService ?? | ||||
|             throw new ArgumentNullException("IAuthenticationService not injected"); | ||||
|         _authStateProvider = authStateProvider ?? | ||||
| @ -39,14 +48,27 @@ public class ApiHttpClientHandler : DelegatingHandler { | ||||
|                                                                  CancellationToken cancellationToken) { | ||||
|         AuthTokens? authTokens = await _authService.GetAuthTokens(); | ||||
|  | ||||
|         if (authTokens is not null) requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authTokens.JwtToken); | ||||
|         HttpRequestMessage initialRequestMessage = new() {  | ||||
|             Content = requestMessage.Content, | ||||
|             Method = requestMessage.Method, | ||||
|             RequestUri = requestMessage.RequestUri | ||||
|         }; | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await base.SendAsync(requestMessage, cancellationToken); | ||||
|         if (authTokens is not null) { | ||||
|             initialRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authTokens.JwtToken); | ||||
|             requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authTokens.JwtToken); | ||||
|         } | ||||
|  | ||||
|         HttpClient initialClient = _httpClientFactory.CreateClient("API_Handler"); | ||||
|  | ||||
|         HttpResponseMessage responseMessage = await initialClient.SendAsync(initialRequestMessage, cancellationToken); | ||||
|  | ||||
|         HttpClient refreshClient = _httpClientFactory.CreateClient("API_Handler"); | ||||
|  | ||||
|         if (responseMessage.StatusCode.Equals(HttpStatusCode.Unauthorized)) { | ||||
|             string? loginId = await _authService.GetLoginId(); | ||||
|  | ||||
|             if (!string.IsNullOrWhiteSpace(loginId) && authTokens is not null) { | ||||
|             if (!string.IsNullOrWhiteSpace(loginId)) { | ||||
|                 AuthAttempt authAttempt = new() { | ||||
|                     LoginID = loginId, | ||||
|                     AuthTokens = authTokens | ||||
| @ -54,29 +76,25 @@ public class ApiHttpClientHandler : DelegatingHandler { | ||||
|  | ||||
|                 HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "auth/refresh"); | ||||
|  | ||||
|                 request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authTokens.JwtToken); | ||||
|  | ||||
|                 request.Content = new StringContent(JsonSerializer.Serialize(authAttempt), | ||||
|                                                     Encoding.UTF8, | ||||
|                                                     "application/json"); | ||||
|  | ||||
|                 HttpClient httpClient = new HttpClient() { | ||||
|                     BaseAddress = new Uri(_apiBaseUrl) | ||||
|                 }; | ||||
|                 if (authTokens is not null) { | ||||
|                     request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authTokens.JwtToken); | ||||
|                 } | ||||
|  | ||||
|                 httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*")); | ||||
|                 HttpResponseMessage httpResponseMessage = await refreshClient.SendAsync(request, cancellationToken); | ||||
|  | ||||
|                 string responseContent = await httpResponseMessage.Content.ReadAsStringAsync(); | ||||
|                  | ||||
|                 HttpResponseMessage httpResponseMessage = await httpClient.SendAsync(request, cancellationToken); | ||||
|  | ||||
|                 if (httpResponseMessage.IsSuccessStatusCode) { | ||||
|                     string responseContent = await httpResponseMessage.Content.ReadAsStringAsync(); | ||||
|  | ||||
|                     JsonSerializerOptions jsonSerializerOptions = new() { | ||||
|                         PropertyNameCaseInsensitive = true | ||||
|                     }; | ||||
|  | ||||
|                     LoginResult loginResult = JsonSerializer.Deserialize<LoginResult>(responseContent, jsonSerializerOptions) ?? | ||||
|                         throw new Exception("Unable to parse login result from API response"); | ||||
|                         throw new Exception("unable to parse login result from API response"); | ||||
|  | ||||
|                     if (!loginResult.IsAuthenticated) throw new Exception($"User with Login ID {loginId} not authorized"); | ||||
|  | ||||
| @ -91,6 +109,7 @@ public class ApiHttpClientHandler : DelegatingHandler { | ||||
|                     await _authStateProvider.Logout(); | ||||
|  | ||||
|                     string? redirectUrl = _cache.Get<string>("redirectUrl"); | ||||
|  | ||||
|                     if (!string.IsNullOrWhiteSpace(redirectUrl)) { | ||||
|                         _navigationManager.NavigateTo($"login/{redirectUrl}"); | ||||
|                     } else { | ||||
| @ -102,6 +121,9 @@ public class ApiHttpClientHandler : DelegatingHandler { | ||||
|             return await base.SendAsync(requestMessage, cancellationToken); | ||||
|         } | ||||
|  | ||||
|         initialClient.Dispose(); | ||||
|         refreshClient.Dispose(); | ||||
|  | ||||
|         return responseMessage; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -12,7 +12,7 @@ | ||||
| @using MesaFabApproval.Client.Layout | ||||
| @using MesaFabApproval.Client.Pages.Components | ||||
| @using MesaFabApproval.Client.Services | ||||
| @using MesaFabApproval.Client.Util | ||||
| @using MesaFabApproval.Shared.Utilities | ||||
| @using MesaFabApproval.Shared.Models | ||||
| @using MudBlazor | ||||
| @using Microsoft.AspNetCore.Authorization | ||||
|  | ||||
							
								
								
									
										5
									
								
								MesaFabApproval.Client/wwwroot/js/OpenInNewWindow.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								MesaFabApproval.Client/wwwroot/js/OpenInNewWindow.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| export function OpenInNewWindow(url, message) { | ||||
|     var newwindow = window.open('', '_blank'); | ||||
|     newwindow.document.write(message); | ||||
|     newwindow.location.href = url; | ||||
| } | ||||
							
								
								
									
										262
									
								
								MesaFabApproval.Shared/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								MesaFabApproval.Shared/.editorconfig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,262 @@ | ||||
| # Remove the line below if you want to inherit .editorconfig settings from higher directories | ||||
| root = true | ||||
|  | ||||
| # C# files | ||||
| [*.cs] | ||||
|  | ||||
| #### Core EditorConfig Options #### | ||||
|  | ||||
| # Indentation and spacing | ||||
| indent_size = 4 | ||||
| indent_style = space | ||||
| tab_width = 4 | ||||
|  | ||||
| # New line preferences | ||||
| end_of_line = crlf | ||||
| insert_final_newline = false | ||||
|  | ||||
| #### .NET Coding Conventions #### | ||||
|  | ||||
| # Organize usings | ||||
| dotnet_separate_import_directive_groups = true | ||||
| dotnet_sort_system_directives_first = true | ||||
| file_header_template = unset | ||||
|  | ||||
| # this. and Me. preferences | ||||
| dotnet_style_qualification_for_event = true | ||||
| dotnet_style_qualification_for_field = true | ||||
| dotnet_style_qualification_for_method = true | ||||
| dotnet_style_qualification_for_property = true | ||||
|  | ||||
| # Language keywords vs BCL types preferences | ||||
| dotnet_style_predefined_type_for_locals_parameters_members = true | ||||
| dotnet_style_predefined_type_for_member_access = true | ||||
|  | ||||
| # Parentheses preferences | ||||
| dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:error | ||||
| dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:error | ||||
| dotnet_style_parentheses_in_other_operators = never_if_unnecessary | ||||
| dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:error | ||||
|  | ||||
| # Modifier preferences | ||||
| dotnet_style_require_accessibility_modifiers = for_non_interface_members | ||||
|  | ||||
| # Expression-level preferences | ||||
| dotnet_style_coalesce_expression = false | ||||
| dotnet_style_collection_initializer = true | ||||
| dotnet_style_explicit_tuple_names = true:error | ||||
| dotnet_style_namespace_match_folder = true | ||||
| dotnet_style_null_propagation = true | ||||
| dotnet_style_object_initializer = true | ||||
| dotnet_style_operator_placement_when_wrapping = beginning_of_line | ||||
| dotnet_style_prefer_auto_properties = true | ||||
| dotnet_style_prefer_compound_assignment = true | ||||
| dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion | ||||
| dotnet_style_prefer_conditional_expression_over_return = true:suggestion | ||||
| dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed | ||||
| dotnet_style_prefer_inferred_anonymous_type_member_names = false | ||||
| dotnet_style_prefer_inferred_tuple_names = false | ||||
| dotnet_style_prefer_is_null_check_over_reference_equality_method = true | ||||
| dotnet_style_prefer_simplified_boolean_expressions = true | ||||
| dotnet_style_prefer_simplified_interpolation = true | ||||
|  | ||||
| # Field preferences | ||||
| dotnet_style_readonly_field = true:warning | ||||
|  | ||||
| # Parameter preferences | ||||
| dotnet_code_quality_unused_parameters = all:error | ||||
|  | ||||
| # Suppression preferences | ||||
| dotnet_remove_unnecessary_suppression_exclusions = none | ||||
|  | ||||
| # New line preferences | ||||
| dotnet_style_allow_multiple_blank_lines_experimental = false:error | ||||
| dotnet_style_allow_statement_immediately_after_block_experimental = false:warning | ||||
|  | ||||
| #### C# Coding Conventions #### | ||||
|  | ||||
| # var preferences | ||||
| csharp_style_var_elsewhere = false:error | ||||
| csharp_style_var_for_built_in_types = false:error | ||||
| csharp_style_var_when_type_is_apparent = false:error | ||||
|  | ||||
| # Expression-bodied members | ||||
| csharp_style_expression_bodied_accessors = false | ||||
| csharp_style_expression_bodied_constructors = false | ||||
| csharp_style_expression_bodied_indexers = false | ||||
| csharp_style_expression_bodied_lambdas = true | ||||
| csharp_style_expression_bodied_local_functions = false | ||||
| csharp_style_expression_bodied_methods = when_on_single_line:suggestion | ||||
| csharp_style_expression_bodied_operators = false | ||||
| csharp_style_expression_bodied_properties = false | ||||
|  | ||||
| # Pattern matching preferences | ||||
| csharp_style_pattern_matching_over_as_with_null_check = false | ||||
| csharp_style_pattern_matching_over_is_with_cast_check = false | ||||
| csharp_style_prefer_extended_property_pattern = true | ||||
| csharp_style_prefer_not_pattern = true | ||||
| csharp_style_prefer_pattern_matching = true | ||||
| csharp_style_prefer_switch_expression = false | ||||
|  | ||||
| # Null-checking preferences | ||||
| csharp_style_conditional_delegate_call = false | ||||
|  | ||||
| # Modifier preferences | ||||
| csharp_prefer_static_local_function = false | ||||
| csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async | ||||
| csharp_style_prefer_readonly_struct = true:warning | ||||
| csharp_style_prefer_readonly_struct_member = true | ||||
|  | ||||
| # Code-block preferences | ||||
| csharp_prefer_braces = when_multiline:error | ||||
| csharp_prefer_simple_using_statement = false | ||||
| csharp_style_namespace_declarations = file_scoped:error | ||||
| csharp_style_prefer_method_group_conversion = true:suggestion | ||||
| csharp_style_prefer_top_level_statements = true:error | ||||
|  | ||||
| # Expression-level preferences | ||||
| csharp_prefer_simple_default_expression = true | ||||
| csharp_style_deconstructed_variable_declaration = false | ||||
| csharp_style_implicit_object_creation_when_type_is_apparent = false | ||||
| csharp_style_inlined_variable_declaration = true | ||||
| csharp_style_prefer_index_operator = false:error | ||||
| csharp_style_prefer_local_over_anonymous_function = true:error | ||||
| csharp_style_prefer_null_check_over_type_check = true | ||||
| csharp_style_prefer_range_operator = false:error | ||||
| csharp_style_prefer_tuple_swap = true | ||||
| csharp_style_prefer_utf8_string_literals = true | ||||
| csharp_style_throw_expression = false | ||||
| csharp_style_unused_value_assignment_preference = unused_local_variable | ||||
| csharp_style_unused_value_expression_statement_preference = unused_local_variable | ||||
|  | ||||
| # 'using' directive preferences | ||||
| csharp_using_directive_placement = outside_namespace:error | ||||
|  | ||||
| # New line preferences | ||||
| csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true | ||||
| csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true | ||||
| csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true | ||||
| csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:error | ||||
| csharp_style_allow_embedded_statements_on_same_line_experimental = true | ||||
|  | ||||
| #### C# Formatting Rules #### | ||||
|  | ||||
| # New line preferences | ||||
| csharp_new_line_before_catch = false | ||||
| csharp_new_line_before_else = false | ||||
| csharp_new_line_before_finally = false | ||||
| csharp_new_line_before_members_in_anonymous_types = true | ||||
| csharp_new_line_before_members_in_object_initializers = true | ||||
| csharp_new_line_before_open_brace = none | ||||
| csharp_new_line_between_query_expression_clauses = true | ||||
|  | ||||
| # Indentation preferences | ||||
| csharp_indent_block_contents = true | ||||
| csharp_indent_braces = false | ||||
| csharp_indent_case_contents = true | ||||
| csharp_indent_case_contents_when_block = true | ||||
| csharp_indent_labels = one_less_than_current | ||||
| csharp_indent_switch_labels = true | ||||
|  | ||||
| # Space preferences | ||||
| csharp_space_after_cast = false | ||||
| csharp_space_after_colon_in_inheritance_clause = true | ||||
| csharp_space_after_comma = true | ||||
| csharp_space_after_dot = false | ||||
| csharp_space_after_keywords_in_control_flow_statements = true | ||||
| csharp_space_after_semicolon_in_for_statement = true | ||||
| csharp_space_around_binary_operators = before_and_after | ||||
| csharp_space_around_declaration_statements = false | ||||
| csharp_space_before_colon_in_inheritance_clause = true | ||||
| csharp_space_before_comma = false | ||||
| csharp_space_before_dot = false | ||||
| csharp_space_before_open_square_brackets = false | ||||
| csharp_space_before_semicolon_in_for_statement = false | ||||
| csharp_space_between_empty_square_brackets = false | ||||
| csharp_space_between_method_call_empty_parameter_list_parentheses = false | ||||
| csharp_space_between_method_call_name_and_opening_parenthesis = false | ||||
| csharp_space_between_method_call_parameter_list_parentheses = false | ||||
| csharp_space_between_method_declaration_empty_parameter_list_parentheses = false | ||||
| csharp_space_between_method_declaration_name_and_open_parenthesis = false | ||||
| csharp_space_between_method_declaration_parameter_list_parentheses = false | ||||
| csharp_space_between_parentheses = false | ||||
| csharp_space_between_square_brackets = false | ||||
|  | ||||
| # Wrapping preferences | ||||
| csharp_preserve_single_line_blocks = true | ||||
| csharp_preserve_single_line_statements = true | ||||
|  | ||||
| #### Naming styles #### | ||||
|  | ||||
| # Naming rules | ||||
|  | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i | ||||
|  | ||||
| dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.types_should_be_pascal_case.symbols = types | ||||
| dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| # Symbol specifications | ||||
|  | ||||
| dotnet_naming_symbols.interface.applicable_kinds = interface | ||||
| dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.interface.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum | ||||
| dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.types.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method | ||||
| dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.non_field_members.required_modifiers =  | ||||
|  | ||||
| # Naming styles | ||||
|  | ||||
| dotnet_naming_style.pascal_case.required_prefix =  | ||||
| dotnet_naming_style.pascal_case.required_suffix =  | ||||
| dotnet_naming_style.pascal_case.word_separator =  | ||||
| dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.begins_with_i.required_prefix = I | ||||
| dotnet_naming_style.begins_with_i.required_suffix =  | ||||
| dotnet_naming_style.begins_with_i.word_separator =  | ||||
| dotnet_naming_style.begins_with_i.capitalization = pascal_case | ||||
|  | ||||
| # Question | ||||
|  | ||||
| dotnet_diagnostic.ASP0025.severity = none # Use AddAuthorizationBuilder to register authorization services and construct policies [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1510.severity = none # Use 'ArgumentNullException.ThrowIfNull' instead of explicitly throwing a new exception instance [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1806.severity = none # 'MRBNumberIsValid' calls 'Append' but does not use the value the method returns. Linq methods are known to not have side effects. Use the result in a conditional statement, assign the result to a variable, or pass it as an argument to another method. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1822.severity = none # Member 'GenerateRefreshToken' does not access instance data and can be marked as static [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1825.severity = none # Avoid unnecessary zero-length array allocations.  Use Array.Empty<string>() instead. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1827.severity = none # Count() is used where Any() could be used instead to improve performance [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1829.severity = none # Use the \"Count\" property instead of Enumerable.Count() [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA1861.severity = none # Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA2208.severity = none # Method UpdateMRBAction passes 'MRB action cannot be null' as the paramName argument to a ArgumentNullException constructor. Replace this argument with one of the method's parameter names. Note that the provided parameter name should have the exact casing as declared on the method. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CA2211.severity = none # Non-constant fields should not be visible | ||||
| dotnet_diagnostic.CA2254.severity = none # The type or namespace name 'AspNetCore' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?) [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.CS0234.severity = none # The type or namespace name 'Extensions' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?) [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.CS0246.severity = none # The type or namespace name 'ILogger<>' could not be found (are you missing a using directive or an assembly reference?) [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.CS1998.severity = none # This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CS8019.severity = none # Unnecessary using directive | ||||
| dotnet_diagnostic.CS8600.severity = none # Converting null literal or possible null value to non-nullable type. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CS8625.severity = none # Cannot convert null literal to non-nullable reference type. [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.CS8765.severity = none # Nullability of type of parameter 'obj' doesn't match overridden member (possibly because of nullability attributes). [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.IDE0001.severity = none # Name can be simplified | ||||
| dotnet_diagnostic.IDE0017.severity = none # Object initialization can be simplified [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0022.severity = none # Use expression body for method [MesaFabApproval.Shared]", | ||||
| dotnet_diagnostic.IDE0028.severity = none # Collection initialization can be simplified [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0044.severity = none # Make field readonly [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0046.severity = none # "'if' statement can be simplified [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0052.severity = none # Private member 'CustomerController._cache' can be removed as the value assigned to it is never read [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0074.severity = none # Use compound assignment [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE0305.severity = none # Collection initialization can be simplified [MesaFabApproval.API]", | ||||
| dotnet_diagnostic.IDE1006.severity = none # Naming rule violation: These words must begin with upper case characters | ||||
| dotnet_style_prefer_conditional_expression_over_return = false | ||||
							
								
								
									
										1
									
								
								MesaFabApproval.Shared/.vscode/format-report.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								MesaFabApproval.Shared/.vscode/format-report.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| [] | ||||
							
								
								
									
										6
									
								
								MesaFabApproval.Shared/.vscode/mklink.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								MesaFabApproval.Shared/.vscode/mklink.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| # mklink | ||||
|  | ||||
| ```bash 1731705389065 = 638673021890650000 = Fri Nov 15 2024 14:16:28 GMT-0700 (Mountain Standard Time) | ||||
| mklink /J "L:\DevOps\Mesa_FI\MesaFabApproval\MesaFabApproval.API\.vscode\.UserSecrets" "C:\Users\phares\AppData\Roaming\Microsoft\UserSecrets\0b98e1f2-95ed-4edd-8149-58cce51ca059" | ||||
| dotnet run --urls=https://localhost:7114/ -C Release | ||||
| ``` | ||||
							
								
								
									
										429
									
								
								MesaFabApproval.Shared/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										429
									
								
								MesaFabApproval.Shared/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,429 @@ | ||||
| { | ||||
|   "[markdown]": { | ||||
|     "editor.wordWrap": "off" | ||||
|   }, | ||||
|   "files.exclude": { | ||||
|     "**/.git": false, | ||||
|     "**/node_modules": true | ||||
|   }, | ||||
|   "files.watcherExclude": { | ||||
|     "**/node_modules": true | ||||
|   }, | ||||
|   "cSpell.words": [ | ||||
|     "abutton", | ||||
|     "accessibilities", | ||||
|     "accodingly", | ||||
|     "acknowledgmentby", | ||||
|     "Acks", | ||||
|     "actionsheet", | ||||
|     "Additonal", | ||||
|     "Addtional", | ||||
|     "againm", | ||||
|     "agendaview", | ||||
|     "AHPS", | ||||
|     "Akrion", | ||||
|     "Antlr", | ||||
|     "Appover", | ||||
|     "Appprrovers", | ||||
|     "Approvalog", | ||||
|     "Aprovers", | ||||
|     "Aproverslist", | ||||
|     "asax", | ||||
|     "aspnetmvc", | ||||
|     "Assignedto", | ||||
|     "Atachments", | ||||
|     "Attachemnt", | ||||
|     "Attachemnts", | ||||
|     "Attchment", | ||||
|     "auditee", | ||||
|     "Auditee", | ||||
|     "Auditees", | ||||
|     "automaically", | ||||
|     "Autthorized", | ||||
|     "beacuase", | ||||
|     "beforeunload", | ||||
|     "Belguim", | ||||
|     "bfound", | ||||
|     "bgcolor", | ||||
|     "Bies", | ||||
|     "binded", | ||||
|     "Biorad", | ||||
|     "blackbackground", | ||||
|     "blackhover", | ||||
|     "blackpressed", | ||||
|     "blueenergy", | ||||
|     "blueopal", | ||||
|     "buttongroup", | ||||
|     "BYMRB", | ||||
|     "bytesgot", | ||||
|     "CAID", | ||||
|     "casection", | ||||
|     "CAXXXX", | ||||
|     "CCPCR", | ||||
|     "CCPCRB", | ||||
|     "cellspacing", | ||||
|     "Cheeso's", | ||||
|     "chkbx", | ||||
|     "Clib", | ||||
|     "colorpicker", | ||||
|     "columnmenu", | ||||
|     "columnsreorder", | ||||
|     "columnsresize", | ||||
|     "comming", | ||||
|     "Containmen", | ||||
|     "Copmments", | ||||
|     "correctiv", | ||||
|     "Correctivet", | ||||
|     "Creat", | ||||
|     "currentd", | ||||
|     "Cyle", | ||||
|     "dadada", | ||||
|     "darkbluehover", | ||||
|     "darkbluepressed", | ||||
|     "darkred", | ||||
|     "datafields", | ||||
|     "datasource", | ||||
|     "dataviz", | ||||
|     "datepicker", | ||||
|     "datetimepicker", | ||||
|     "dayview", | ||||
|     "Deletet", | ||||
|     "Delgation", | ||||
|     "DENITED", | ||||
|     "Deparmtent", | ||||
|     "departmentids", | ||||
|     "Descirption", | ||||
|     "devprog", | ||||
|     "dfeffc", | ||||
|     "Disp", | ||||
|     "Dispo", | ||||
|     "Dispoitio", | ||||
|     "Dispos", | ||||
|     "Dispositon", | ||||
|     "Dispostion", | ||||
|     "Dispostions", | ||||
|     "dispotypevalidation", | ||||
|     "Docbase", | ||||
|     "Documentum", | ||||
|     "Documetum", | ||||
|     "dont", | ||||
|     "downlaoded", | ||||
|     "draganddrop", | ||||
|     "dragcancel", | ||||
|     "dropdownlist", | ||||
|     "Eamils", | ||||
|     "ECNPCRB", | ||||
|     "Ecns", | ||||
|     "edmx", | ||||
|     "EECN", | ||||
|     "emai", | ||||
|     "emailparams", | ||||
|     "Emergrncy", | ||||
|     "energyblue", | ||||
|     "Eran", | ||||
|     "Esql", | ||||
|     "ETECN", | ||||
|     "EXCELOPENXML", | ||||
|     "existinglocation", | ||||
|     "Expando", | ||||
|     "extrafield", | ||||
|     "fadc", | ||||
|     "fbec", | ||||
|     "fcfdfd", | ||||
|     "fdff", | ||||
|     "fece", | ||||
|     "feeebd", | ||||
|     "ffdc", | ||||
|     "ffdd", | ||||
|     "fieldset", | ||||
|     "FILIPE", | ||||
|     "filtermenu", | ||||
|     "Fldr", | ||||
|     "flintstone", | ||||
|     "FLOWLOCS", | ||||
|     "FMEA", | ||||
|     "ftplib", | ||||
|     "FTPSPN", | ||||
|     "GETDATE", | ||||
|     "gitea", | ||||
|     "globaldocudms", | ||||
|     "glyphicons", | ||||
|     "groupable", | ||||
|     "Guids", | ||||
|     "halflings", | ||||
|     "Hexsize", | ||||
|     "highcontrast", | ||||
|     "Hmac", | ||||
|     "holdsteps", | ||||
|     "hostspecific", | ||||
|     "icenium", | ||||
|     "IECN", | ||||
|     "imagebrowser", | ||||
|     "IMRB", | ||||
|     "Infineon", | ||||
|     "Insertd", | ||||
|     "inverseicons", | ||||
|     "IPCRB", | ||||
|     "ISADMIN", | ||||
|     "islast", | ||||
|     "ISNULL", | ||||
|     "ITAR", | ||||
|     "jquery", | ||||
|     "jqueryval", | ||||
|     "jqwidgets", | ||||
|     "jqxbuttongroup", | ||||
|     "jqxbuttons", | ||||
|     "jqxcalendar", | ||||
|     "jqxchart", | ||||
|     "jqxcheckbox", | ||||
|     "jqxcolorpicker", | ||||
|     "jqxcombobox", | ||||
|     "jqxcore", | ||||
|     "jqxdata", | ||||
|     "jqxdatatable", | ||||
|     "jqxdatetimeinput", | ||||
|     "jqxdocking", | ||||
|     "jqxdockpanel", | ||||
|     "jqxdragdrop", | ||||
|     "jqxdropdownbutton", | ||||
|     "jqxdropdownlist", | ||||
|     "jqxexpander", | ||||
|     "jqxgauge", | ||||
|     "jqxgrid", | ||||
|     "jqxinput", | ||||
|     "jqxknockout", | ||||
|     "jqxlistbox", | ||||
|     "jqxlistmenu", | ||||
|     "jqxmaskedinput", | ||||
|     "jqxmenu", | ||||
|     "jqxnavigationbar", | ||||
|     "jqxnumberinput", | ||||
|     "jqxpanel", | ||||
|     "jqxpasswordinput", | ||||
|     "jqxprogressbar", | ||||
|     "jqxradiobutton", | ||||
|     "jqxrangeselector", | ||||
|     "jqxrating", | ||||
|     "jqxresponse", | ||||
|     "jqxscrollbar", | ||||
|     "jqxscrollview", | ||||
|     "jqxslider", | ||||
|     "jqxsplitter", | ||||
|     "jqxswitchbutton", | ||||
|     "jqxtabs", | ||||
|     "jqxtooltip", | ||||
|     "jqxtouch", | ||||
|     "jqxtree", | ||||
|     "jqxtreegrid", | ||||
|     "jqxtreemap", | ||||
|     "jqxvalidator", | ||||
|     "jqxwindow", | ||||
|     "kendogridcustom", | ||||
|     "kendoui", | ||||
|     "labelelement", | ||||
|     "labelledby", | ||||
|     "Leanred", | ||||
|     "lightgray", | ||||
|     "linkbutton", | ||||
|     "Linq", | ||||
|     "Listdiv", | ||||
|     "listview", | ||||
|     "Lnks", | ||||
|     "localfilename", | ||||
|     "loclist", | ||||
|     "logis", | ||||
|     "logtext", | ||||
|     "loopmis", | ||||
|     "lotdispo", | ||||
|     "LOTDISPSITION", | ||||
|     "Lotfile", | ||||
|     "lotlist", | ||||
|     "lotstatusoption", | ||||
|     "LTRIM", | ||||
|     "MADUREIRA", | ||||
|     "mailrelay", | ||||
|     "MDTM", | ||||
|     "meego", | ||||
|     "meetingid", | ||||
|     "menubutton", | ||||
|     "mesafi", | ||||
|     "metroblack", | ||||
|     "metrodark", | ||||
|     "miliseconds", | ||||
|     "modalview", | ||||
|     "modernizr", | ||||
|     "Modernizr", | ||||
|     "monthview", | ||||
|     "MRBIs", | ||||
|     "Mrbs", | ||||
|     "msecs", | ||||
|     "multipleextended", | ||||
|     "Navigatable", | ||||
|     "nbsp", | ||||
|     "newbase", | ||||
|     "newchange", | ||||
|     "newdi", | ||||
|     "newfilename", | ||||
|     "newsource", | ||||
|     "Newtonsoft", | ||||
|     "Nontransferrable", | ||||
|     "notications", | ||||
|     "Notifcation", | ||||
|     "Notifyf", | ||||
|     "NTLM", | ||||
|     "Nullcc", | ||||
|     "numerictextbox", | ||||
|     "objdata", | ||||
|     "OCAP", | ||||
|     "occured", | ||||
|     "odata", | ||||
|     "oldfilename", | ||||
|     "OLHOLD", | ||||
|     "onclick", | ||||
|     "onmousemove", | ||||
|     "OPDESC", | ||||
|     "OPENQUERY", | ||||
|     "Oper", | ||||
|     "operationslist", | ||||
|     "Orginator", | ||||
|     "Originatorname", | ||||
|     "Ouellette", | ||||
|     "Owin", | ||||
|     "pageable", | ||||
|     "Pageable", | ||||
|     "panelbar", | ||||
|     "parentid", | ||||
|     "parminput", | ||||
|     "parms", | ||||
|     "Parms", | ||||
|     "particula", | ||||
|     "pasv", | ||||
|     "PASV", | ||||
|     "PATINDEX", | ||||
|     "PCRB", | ||||
|     "PCRBID", | ||||
|     "pcrvalues", | ||||
|     "pdbonly", | ||||
|     "Preventitive", | ||||
|     "preventivet", | ||||
|     "Prevetative", | ||||
|     "proces", | ||||
|     "Processedl", | ||||
|     "procs", | ||||
|     "productfamilies", | ||||
|     "progess", | ||||
|     "progressbar", | ||||
|     "qrcode", | ||||
|     "Quanityt", | ||||
|     "rangebar", | ||||
|     "Recep", | ||||
|     "Recepient", | ||||
|     "recieved", | ||||
|     "recordlock", | ||||
|     "remotefilename", | ||||
|     "reorderable", | ||||
|     "reportform", | ||||
|     "reportslist", | ||||
|     "reportslistdiv", | ||||
|     "Reqquired", | ||||
|     "Reqs", | ||||
|     "Requiest", | ||||
|     "Responsibles", | ||||
|     "RETR", | ||||
|     "Revisioing", | ||||
|     "Revisioned", | ||||
|     "Revison", | ||||
|     "rgba", | ||||
|     "rkotian", | ||||
|     "RNFR", | ||||
|     "RNTO", | ||||
|     "Roless", | ||||
|     "roundbg", | ||||
|     "RTRIM", | ||||
|     "SAMDB", | ||||
|     "scroller", | ||||
|     "scrollview", | ||||
|     "seleced", | ||||
|     "selectionlog", | ||||
|     "Selectpart", | ||||
|     "sess", | ||||
|     "Sfisharepoint", | ||||
|     "shinyblack", | ||||
|     "showpassword", | ||||
|     "SIGNON", | ||||
|     "simpleparser", | ||||
|     "slddrw", | ||||
|     "sldprt", | ||||
|     "sortasc", | ||||
|     "sortascbutton", | ||||
|     "sortdesc", | ||||
|     "sortdescbutton", | ||||
|     "sortremove", | ||||
|     "sparkline", | ||||
|     "splitview", | ||||
|     "SPNMRB", | ||||
|     "SPNPDB", | ||||
|     "SSRS", | ||||
|     "Sssign", | ||||
|     "Staus", | ||||
|     "stylesheet", | ||||
|     "Submited", | ||||
|     "subrole", | ||||
|     "subroles", | ||||
|     "Succefully", | ||||
|     "Succesfully", | ||||
|     "sucessfully", | ||||
|     "SURP", | ||||
|     "Swashbuckle", | ||||
|     "SWRN", | ||||
|     "tabindex", | ||||
|     "tabstrip", | ||||
|     "Tahoma", | ||||
|     "taskcompleted", | ||||
|     "Tasklist", | ||||
|     "Taveler", | ||||
|     "TECN", | ||||
|     "TECNs", | ||||
|     "TEMIRWAP", | ||||
|     "tempecd", | ||||
|     "tempimplement", | ||||
|     "templabel", | ||||
|     "tempvalue", | ||||
|     "TEMSA", | ||||
|     "Tencor", | ||||
|     "timepicker", | ||||
|     "Tobe", | ||||
|     "Toplevel", | ||||
|     "Totrav", | ||||
|     "trainingby", | ||||
|     "Traininglist", | ||||
|     "traininglistdiv", | ||||
|     "transanction", | ||||
|     "Trav", | ||||
|     "Traveller", | ||||
|     "Traverler", | ||||
|     "TRAVLELER", | ||||
|     "Travler", | ||||
|     "TREEVIEW", | ||||
|     "trigerred", | ||||
|     "ttinclude", | ||||
|     "Uhandled", | ||||
|     "Updat", | ||||
|     "Uplaod", | ||||
|     "Upto", | ||||
|     "userevents", | ||||
|     "userids", | ||||
|     "userlist", | ||||
|     "Validatable", | ||||
|     "valueelement", | ||||
|     "Variabls", | ||||
|     "Verdana", | ||||
|     "vgrid", | ||||
|     "viewmodel", | ||||
|     "vsdoc", | ||||
|     "whethere", | ||||
|     "windowsphone", | ||||
|     "Winsock", | ||||
|     "worlflow" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										42
									
								
								MesaFabApproval.Shared/.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								MesaFabApproval.Shared/.vscode/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| { | ||||
|     "version": "2.0.0", | ||||
|     "tasks": [ | ||||
|         { | ||||
|             "label": "Format", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "--report", | ||||
|                 ".vscode", | ||||
|                 "--verbosity", | ||||
|                 "detailed", | ||||
|                 "--severity", | ||||
|                 "warn" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "Format-Whitespaces", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "format", | ||||
|                 "whitespace" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         }, | ||||
|         { | ||||
|             "label": "build", | ||||
|             "command": "dotnet", | ||||
|             "type": "process", | ||||
|             "args": [ | ||||
|                 "build", | ||||
|                 "${workspaceFolder}/MesaFabApproval.Shared.csproj", | ||||
|                 "/property:GenerateFullPaths=true", | ||||
|                 "/consoleloggerparameters:NoSummary" | ||||
|             ], | ||||
|             "problemMatcher": "$msCompile" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @ -7,7 +7,7 @@ | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.6" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> | ||||
|     <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" /> | ||||
|  | ||||
| @ -1,4 +1,6 @@ | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
| using MesaFabApproval.Shared.Utilities; | ||||
|  | ||||
| namespace MesaFabApproval.Shared.Models; | ||||
|  | ||||
| public class Approval { | ||||
|     public int ApprovalID { get; set; } | ||||
| @ -10,11 +12,11 @@ public class Approval { | ||||
|     public required int SubRoleID { get; set; } | ||||
|     public int ItemStatus { get; set; } = 0; | ||||
|     public string StatusMessage = "Assigned"; | ||||
|     public DateTime NotifyDate { get; set; } = DateTime.MinValue; | ||||
|     public required DateTime AssignedDate {  get; set; } | ||||
|     public DateTime CompletedDate { get; set; } = DateTime.MaxValue; | ||||
|     public DateTime NotifyDate { get; set; } = DateTimeUtilities.MIN_DT; | ||||
|     public required DateTime AssignedDate { get; set; } | ||||
|     public DateTime CompletedDate { get; set; } = DateTimeUtilities.MAX_DT; | ||||
|     public string Comments { get; set; } = ""; | ||||
|     public int Step { get; set; } = 1; | ||||
|     public string SubRoleCategoryItem { get; set; } = ""; | ||||
|     public int TaskID { get; set; } | ||||
| } | ||||
| } | ||||
| @ -4,4 +4,4 @@ public class AuthAttempt { | ||||
|     public required string LoginID { get; set; } | ||||
|     public string Password { get; set; } = ""; | ||||
|     public AuthTokens? AuthTokens { get; set; } | ||||
| } | ||||
| } | ||||
| @ -3,4 +3,4 @@ | ||||
| public class AuthTokens { | ||||
|     public required string JwtToken { get; set; } | ||||
|     public required string RefreshToken { get; set; } | ||||
| } | ||||
| } | ||||
| @ -4,4 +4,4 @@ public class LoginResult { | ||||
|     public required bool IsAuthenticated { get; set; } | ||||
|     public required User User { get; set; } | ||||
|     public required AuthTokens AuthTokens { get; set; } | ||||
| } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	