Merge branch 'main' into dev
2
.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
*.log
|
|
||||||
*.db
|
|
||||||
11
backend/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
main
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
go.work
|
||||||
|
*.log
|
||||||
|
*.db
|
||||||
694
frontend/.editorconfig
Normal file
|
|
@ -0,0 +1,694 @@
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = false
|
||||||
|
max_line_length = 100
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_formatter_off_tag = @formatter:off
|
||||||
|
ij_formatter_on_tag = @formatter:on
|
||||||
|
ij_formatter_tags_enabled = true
|
||||||
|
ij_smart_tabs = false
|
||||||
|
ij_visual_guides = 100
|
||||||
|
ij_wrap_on_typing = false
|
||||||
|
|
||||||
|
[*.css]
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_css_align_closing_brace_with_properties = false
|
||||||
|
ij_css_blank_lines_around_nested_selector = 1
|
||||||
|
ij_css_blank_lines_between_blocks = 1
|
||||||
|
ij_css_brace_placement = end_of_line
|
||||||
|
ij_css_enforce_quotes_on_format = false
|
||||||
|
ij_css_hex_color_long_format = false
|
||||||
|
ij_css_hex_color_lower_case = false
|
||||||
|
ij_css_hex_color_short_format = false
|
||||||
|
ij_css_hex_color_upper_case = false
|
||||||
|
ij_css_keep_blank_lines_in_code = 2
|
||||||
|
ij_css_keep_indents_on_empty_lines = false
|
||||||
|
ij_css_keep_single_line_blocks = false
|
||||||
|
ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
|
||||||
|
ij_css_space_after_colon = true
|
||||||
|
ij_css_space_before_opening_brace = true
|
||||||
|
ij_css_use_double_quotes = true
|
||||||
|
ij_css_value_alignment = do_not_align
|
||||||
|
|
||||||
|
[*.ejs]
|
||||||
|
ij_visual_guides = none
|
||||||
|
|
||||||
|
[*.feature]
|
||||||
|
tab_width = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_gherkin_keep_indents_on_empty_lines = false
|
||||||
|
|
||||||
|
[*.haml]
|
||||||
|
tab_width = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_haml_keep_indents_on_empty_lines = false
|
||||||
|
|
||||||
|
[*.less]
|
||||||
|
tab_width = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_less_align_closing_brace_with_properties = false
|
||||||
|
ij_less_blank_lines_around_nested_selector = 1
|
||||||
|
ij_less_blank_lines_between_blocks = 1
|
||||||
|
ij_less_brace_placement = 0
|
||||||
|
ij_less_enforce_quotes_on_format = false
|
||||||
|
ij_less_hex_color_long_format = false
|
||||||
|
ij_less_hex_color_lower_case = false
|
||||||
|
ij_less_hex_color_short_format = false
|
||||||
|
ij_less_hex_color_upper_case = false
|
||||||
|
ij_less_keep_blank_lines_in_code = 2
|
||||||
|
ij_less_keep_indents_on_empty_lines = false
|
||||||
|
ij_less_keep_single_line_blocks = false
|
||||||
|
ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
|
||||||
|
ij_less_space_after_colon = true
|
||||||
|
ij_less_space_before_opening_brace = true
|
||||||
|
ij_less_use_double_quotes = true
|
||||||
|
ij_less_value_alignment = 0
|
||||||
|
|
||||||
|
[*.sass]
|
||||||
|
tab_width = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_sass_align_closing_brace_with_properties = false
|
||||||
|
ij_sass_blank_lines_around_nested_selector = 1
|
||||||
|
ij_sass_blank_lines_between_blocks = 1
|
||||||
|
ij_sass_brace_placement = 0
|
||||||
|
ij_sass_enforce_quotes_on_format = false
|
||||||
|
ij_sass_hex_color_long_format = false
|
||||||
|
ij_sass_hex_color_lower_case = false
|
||||||
|
ij_sass_hex_color_short_format = false
|
||||||
|
ij_sass_hex_color_upper_case = false
|
||||||
|
ij_sass_keep_blank_lines_in_code = 2
|
||||||
|
ij_sass_keep_indents_on_empty_lines = false
|
||||||
|
ij_sass_keep_single_line_blocks = false
|
||||||
|
ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
|
||||||
|
ij_sass_space_after_colon = true
|
||||||
|
ij_sass_space_before_opening_brace = true
|
||||||
|
ij_sass_use_double_quotes = true
|
||||||
|
ij_sass_value_alignment = 0
|
||||||
|
|
||||||
|
[*.scss]
|
||||||
|
tab_width = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_scss_align_closing_brace_with_properties = false
|
||||||
|
ij_scss_blank_lines_around_nested_selector = 1
|
||||||
|
ij_scss_blank_lines_between_blocks = 1
|
||||||
|
ij_scss_brace_placement = 0
|
||||||
|
ij_scss_enforce_quotes_on_format = false
|
||||||
|
ij_scss_hex_color_long_format = false
|
||||||
|
ij_scss_hex_color_lower_case = false
|
||||||
|
ij_scss_hex_color_short_format = false
|
||||||
|
ij_scss_hex_color_upper_case = false
|
||||||
|
ij_scss_keep_blank_lines_in_code = 2
|
||||||
|
ij_scss_keep_indents_on_empty_lines = false
|
||||||
|
ij_scss_keep_single_line_blocks = false
|
||||||
|
ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
|
||||||
|
ij_scss_space_after_colon = true
|
||||||
|
ij_scss_space_before_opening_brace = true
|
||||||
|
ij_scss_use_double_quotes = true
|
||||||
|
ij_scss_value_alignment = 0
|
||||||
|
|
||||||
|
[*.styl]
|
||||||
|
tab_width = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_stylus_align_closing_brace_with_properties = false
|
||||||
|
ij_stylus_blank_lines_around_nested_selector = 1
|
||||||
|
ij_stylus_blank_lines_between_blocks = 1
|
||||||
|
ij_stylus_brace_placement = 0
|
||||||
|
ij_stylus_enforce_quotes_on_format = false
|
||||||
|
ij_stylus_hex_color_long_format = false
|
||||||
|
ij_stylus_hex_color_lower_case = false
|
||||||
|
ij_stylus_hex_color_short_format = false
|
||||||
|
ij_stylus_hex_color_upper_case = false
|
||||||
|
ij_stylus_keep_blank_lines_in_code = 2
|
||||||
|
ij_stylus_keep_indents_on_empty_lines = false
|
||||||
|
ij_stylus_keep_single_line_blocks = false
|
||||||
|
ij_stylus_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow
|
||||||
|
ij_stylus_space_after_colon = true
|
||||||
|
ij_stylus_space_before_opening_brace = true
|
||||||
|
ij_stylus_use_double_quotes = true
|
||||||
|
ij_stylus_value_alignment = 0
|
||||||
|
|
||||||
|
[*.vue]
|
||||||
|
ij_continuation_indent_size = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_vue_indent_children_of_top_level = template
|
||||||
|
ij_vue_interpolation_new_line_after_start_delimiter = true
|
||||||
|
ij_vue_interpolation_new_line_before_end_delimiter = true
|
||||||
|
ij_vue_interpolation_wrap = off
|
||||||
|
ij_vue_keep_indents_on_empty_lines = false
|
||||||
|
ij_vue_spaces_within_interpolation_expressions = true
|
||||||
|
ij_vue_uniform_indent = true
|
||||||
|
|
||||||
|
[.editorconfig]
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_editorconfig_align_group_field_declarations = false
|
||||||
|
ij_editorconfig_space_after_colon = false
|
||||||
|
ij_editorconfig_space_after_comma = true
|
||||||
|
ij_editorconfig_space_before_colon = false
|
||||||
|
ij_editorconfig_space_before_comma = false
|
||||||
|
ij_editorconfig_spaces_around_assignment_operators = true
|
||||||
|
|
||||||
|
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_xml_align_attributes = true
|
||||||
|
ij_xml_align_text = false
|
||||||
|
ij_xml_attribute_wrap = normal
|
||||||
|
ij_xml_block_comment_at_first_column = true
|
||||||
|
ij_xml_keep_blank_lines = 2
|
||||||
|
ij_xml_keep_indents_on_empty_lines = false
|
||||||
|
ij_xml_keep_line_breaks = true
|
||||||
|
ij_xml_keep_line_breaks_in_text = true
|
||||||
|
ij_xml_keep_whitespaces = false
|
||||||
|
ij_xml_keep_whitespaces_around_cdata = preserve
|
||||||
|
ij_xml_keep_whitespaces_inside_cdata = false
|
||||||
|
ij_xml_line_comment_at_first_column = true
|
||||||
|
ij_xml_space_after_tag_name = false
|
||||||
|
ij_xml_space_around_equals_in_attribute = false
|
||||||
|
ij_xml_space_inside_empty_tag = false
|
||||||
|
ij_xml_text_wrap = normal
|
||||||
|
|
||||||
|
[{*.ats,*.ts,*.tsx}]
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_typescript_align_imports = false
|
||||||
|
ij_typescript_align_multiline_array_initializer_expression = false
|
||||||
|
ij_typescript_align_multiline_binary_operation = false
|
||||||
|
ij_typescript_align_multiline_chained_methods = true
|
||||||
|
ij_typescript_align_multiline_extends_list = false
|
||||||
|
ij_typescript_align_multiline_for = false
|
||||||
|
ij_typescript_align_multiline_parameters = false
|
||||||
|
ij_typescript_align_multiline_parameters_in_calls = false
|
||||||
|
ij_typescript_align_multiline_ternary_operation = false
|
||||||
|
ij_typescript_align_object_properties = 0
|
||||||
|
ij_typescript_align_union_types = false
|
||||||
|
ij_typescript_align_var_statements = 0
|
||||||
|
ij_typescript_array_initializer_new_line_after_left_brace = true
|
||||||
|
ij_typescript_array_initializer_right_brace_on_new_line = true
|
||||||
|
ij_typescript_array_initializer_wrap = on_every_item
|
||||||
|
ij_typescript_assignment_wrap = on_every_item
|
||||||
|
ij_typescript_binary_operation_sign_on_next_line = true
|
||||||
|
ij_typescript_binary_operation_wrap = on_every_item
|
||||||
|
ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
|
||||||
|
ij_typescript_blank_lines_after_imports = 1
|
||||||
|
ij_typescript_blank_lines_around_class = 1
|
||||||
|
ij_typescript_blank_lines_around_field = 0
|
||||||
|
ij_typescript_blank_lines_around_field_in_interface = 0
|
||||||
|
ij_typescript_blank_lines_around_function = 1
|
||||||
|
ij_typescript_blank_lines_around_method = 1
|
||||||
|
ij_typescript_blank_lines_around_method_in_interface = 1
|
||||||
|
ij_typescript_block_brace_style = end_of_line
|
||||||
|
ij_typescript_call_parameters_new_line_after_left_paren = false
|
||||||
|
ij_typescript_call_parameters_right_paren_on_new_line = false
|
||||||
|
ij_typescript_call_parameters_wrap = on_every_item
|
||||||
|
ij_typescript_catch_on_new_line = false
|
||||||
|
ij_typescript_chained_call_dot_on_new_line = true
|
||||||
|
ij_typescript_class_brace_style = end_of_line
|
||||||
|
ij_typescript_comma_on_new_line = false
|
||||||
|
ij_typescript_do_while_brace_force = always
|
||||||
|
ij_typescript_else_on_new_line = false
|
||||||
|
ij_typescript_enforce_trailing_comma = remove
|
||||||
|
ij_typescript_extends_keyword_wrap = off
|
||||||
|
ij_typescript_extends_list_wrap = off
|
||||||
|
ij_typescript_field_prefix = _
|
||||||
|
ij_typescript_file_name_style = relaxed
|
||||||
|
ij_typescript_finally_on_new_line = false
|
||||||
|
ij_typescript_for_brace_force = always
|
||||||
|
ij_typescript_for_statement_new_line_after_left_paren = false
|
||||||
|
ij_typescript_for_statement_right_paren_on_new_line = false
|
||||||
|
ij_typescript_for_statement_wrap = off
|
||||||
|
ij_typescript_force_quote_style = true
|
||||||
|
ij_typescript_force_semicolon_style = true
|
||||||
|
ij_typescript_function_expression_brace_style = end_of_line
|
||||||
|
ij_typescript_if_brace_force = always
|
||||||
|
ij_typescript_import_merge_members = global
|
||||||
|
ij_typescript_import_prefer_absolute_path = global
|
||||||
|
ij_typescript_import_sort_members = true
|
||||||
|
ij_typescript_import_sort_module_name = false
|
||||||
|
ij_typescript_import_use_node_resolution = true
|
||||||
|
ij_typescript_imports_wrap = on_every_item
|
||||||
|
ij_typescript_indent_case_from_switch = true
|
||||||
|
ij_typescript_indent_chained_calls = true
|
||||||
|
ij_typescript_indent_package_children = 0
|
||||||
|
ij_typescript_jsdoc_include_types = false
|
||||||
|
ij_typescript_jsx_attribute_value = braces
|
||||||
|
ij_typescript_keep_blank_lines_in_code = 1
|
||||||
|
ij_typescript_keep_first_column_comment = true
|
||||||
|
ij_typescript_keep_indents_on_empty_lines = false
|
||||||
|
ij_typescript_keep_line_breaks = true
|
||||||
|
ij_typescript_keep_simple_blocks_in_one_line = true
|
||||||
|
ij_typescript_keep_simple_methods_in_one_line = true
|
||||||
|
ij_typescript_line_comment_add_space = true
|
||||||
|
ij_typescript_line_comment_at_first_column = false
|
||||||
|
ij_typescript_method_brace_style = end_of_line
|
||||||
|
ij_typescript_method_call_chain_wrap = on_every_item
|
||||||
|
ij_typescript_method_parameters_new_line_after_left_paren = false
|
||||||
|
ij_typescript_method_parameters_right_paren_on_new_line = false
|
||||||
|
ij_typescript_method_parameters_wrap = on_every_item
|
||||||
|
ij_typescript_object_literal_wrap = on_every_item
|
||||||
|
ij_typescript_parentheses_expression_new_line_after_left_paren = false
|
||||||
|
ij_typescript_parentheses_expression_right_paren_on_new_line = false
|
||||||
|
ij_typescript_place_assignment_sign_on_next_line = false
|
||||||
|
ij_typescript_prefer_as_type_cast = false
|
||||||
|
ij_typescript_prefer_explicit_types_function_expression_returns = false
|
||||||
|
ij_typescript_prefer_explicit_types_function_returns = false
|
||||||
|
ij_typescript_prefer_explicit_types_vars_fields = false
|
||||||
|
ij_typescript_prefer_parameters_wrap = false
|
||||||
|
ij_typescript_reformat_c_style_comments = true
|
||||||
|
ij_typescript_space_after_colon = true
|
||||||
|
ij_typescript_space_after_comma = true
|
||||||
|
ij_typescript_space_after_dots_in_rest_parameter = false
|
||||||
|
ij_typescript_space_after_generator_mult = true
|
||||||
|
ij_typescript_space_after_property_colon = true
|
||||||
|
ij_typescript_space_after_quest = true
|
||||||
|
ij_typescript_space_after_type_colon = true
|
||||||
|
ij_typescript_space_after_unary_not = false
|
||||||
|
ij_typescript_space_before_async_arrow_lparen = true
|
||||||
|
ij_typescript_space_before_catch_keyword = true
|
||||||
|
ij_typescript_space_before_catch_left_brace = true
|
||||||
|
ij_typescript_space_before_catch_parentheses = true
|
||||||
|
ij_typescript_space_before_class_lbrace = true
|
||||||
|
ij_typescript_space_before_class_left_brace = true
|
||||||
|
ij_typescript_space_before_colon = true
|
||||||
|
ij_typescript_space_before_comma = false
|
||||||
|
ij_typescript_space_before_do_left_brace = true
|
||||||
|
ij_typescript_space_before_else_keyword = true
|
||||||
|
ij_typescript_space_before_else_left_brace = true
|
||||||
|
ij_typescript_space_before_finally_keyword = true
|
||||||
|
ij_typescript_space_before_finally_left_brace = true
|
||||||
|
ij_typescript_space_before_for_left_brace = true
|
||||||
|
ij_typescript_space_before_for_parentheses = true
|
||||||
|
ij_typescript_space_before_for_semicolon = false
|
||||||
|
ij_typescript_space_before_function_left_parenth = true
|
||||||
|
ij_typescript_space_before_generator_mult = false
|
||||||
|
ij_typescript_space_before_if_left_brace = true
|
||||||
|
ij_typescript_space_before_if_parentheses = true
|
||||||
|
ij_typescript_space_before_method_call_parentheses = false
|
||||||
|
ij_typescript_space_before_method_left_brace = true
|
||||||
|
ij_typescript_space_before_method_parentheses = false
|
||||||
|
ij_typescript_space_before_property_colon = false
|
||||||
|
ij_typescript_space_before_quest = true
|
||||||
|
ij_typescript_space_before_switch_left_brace = true
|
||||||
|
ij_typescript_space_before_switch_parentheses = true
|
||||||
|
ij_typescript_space_before_try_left_brace = true
|
||||||
|
ij_typescript_space_before_type_colon = false
|
||||||
|
ij_typescript_space_before_unary_not = false
|
||||||
|
ij_typescript_space_before_while_keyword = true
|
||||||
|
ij_typescript_space_before_while_left_brace = true
|
||||||
|
ij_typescript_space_before_while_parentheses = true
|
||||||
|
ij_typescript_spaces_around_additive_operators = true
|
||||||
|
ij_typescript_spaces_around_arrow_function_operator = true
|
||||||
|
ij_typescript_spaces_around_assignment_operators = true
|
||||||
|
ij_typescript_spaces_around_bitwise_operators = true
|
||||||
|
ij_typescript_spaces_around_equality_operators = true
|
||||||
|
ij_typescript_spaces_around_logical_operators = true
|
||||||
|
ij_typescript_spaces_around_multiplicative_operators = true
|
||||||
|
ij_typescript_spaces_around_relational_operators = true
|
||||||
|
ij_typescript_spaces_around_shift_operators = true
|
||||||
|
ij_typescript_spaces_around_unary_operator = false
|
||||||
|
ij_typescript_spaces_within_array_initializer_brackets = false
|
||||||
|
ij_typescript_spaces_within_brackets = false
|
||||||
|
ij_typescript_spaces_within_catch_parentheses = false
|
||||||
|
ij_typescript_spaces_within_for_parentheses = false
|
||||||
|
ij_typescript_spaces_within_if_parentheses = false
|
||||||
|
ij_typescript_spaces_within_imports = true
|
||||||
|
ij_typescript_spaces_within_interpolation_expressions = false
|
||||||
|
ij_typescript_spaces_within_method_call_parentheses = false
|
||||||
|
ij_typescript_spaces_within_method_parentheses = false
|
||||||
|
ij_typescript_spaces_within_object_literal_braces = true
|
||||||
|
ij_typescript_spaces_within_object_type_braces = true
|
||||||
|
ij_typescript_spaces_within_parentheses = false
|
||||||
|
ij_typescript_spaces_within_switch_parentheses = false
|
||||||
|
ij_typescript_spaces_within_type_assertion = false
|
||||||
|
ij_typescript_spaces_within_union_types = true
|
||||||
|
ij_typescript_spaces_within_while_parentheses = false
|
||||||
|
ij_typescript_special_else_if_treatment = true
|
||||||
|
ij_typescript_ternary_operation_signs_on_next_line = true
|
||||||
|
ij_typescript_ternary_operation_wrap = on_every_item
|
||||||
|
ij_typescript_union_types_wrap = on_every_item
|
||||||
|
ij_typescript_use_chained_calls_group_indents = false
|
||||||
|
ij_typescript_use_double_quotes = false
|
||||||
|
ij_typescript_use_explicit_js_extension = global
|
||||||
|
ij_typescript_use_path_mapping = always
|
||||||
|
ij_typescript_use_public_modifier = false
|
||||||
|
ij_typescript_use_semicolon_after_statement = true
|
||||||
|
ij_typescript_var_declaration_wrap = on_every_item
|
||||||
|
ij_typescript_while_brace_force = always
|
||||||
|
ij_typescript_while_on_new_line = false
|
||||||
|
ij_typescript_wrap_comments = false
|
||||||
|
|
||||||
|
[{*.bash,*.sh,*.zsh}]
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_shell_binary_ops_start_line = false
|
||||||
|
ij_shell_keep_column_alignment_padding = false
|
||||||
|
ij_shell_minify_program = false
|
||||||
|
ij_shell_redirect_followed_by_space = false
|
||||||
|
ij_shell_switch_cases_indented = false
|
||||||
|
|
||||||
|
[{*.cjs,*.js,*.mjs}]
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_javascript_align_imports = false
|
||||||
|
ij_javascript_align_multiline_array_initializer_expression = false
|
||||||
|
ij_javascript_align_multiline_binary_operation = false
|
||||||
|
ij_javascript_align_multiline_chained_methods = true
|
||||||
|
ij_javascript_align_multiline_extends_list = false
|
||||||
|
ij_javascript_align_multiline_for = false
|
||||||
|
ij_javascript_align_multiline_parameters = false
|
||||||
|
ij_javascript_align_multiline_parameters_in_calls = false
|
||||||
|
ij_javascript_align_multiline_ternary_operation = false
|
||||||
|
ij_javascript_align_object_properties = 0
|
||||||
|
ij_javascript_align_union_types = false
|
||||||
|
ij_javascript_align_var_statements = 0
|
||||||
|
ij_javascript_array_initializer_new_line_after_left_brace = true
|
||||||
|
ij_javascript_array_initializer_right_brace_on_new_line = true
|
||||||
|
ij_javascript_array_initializer_wrap = on_every_item
|
||||||
|
ij_javascript_assignment_wrap = on_every_item
|
||||||
|
ij_javascript_binary_operation_sign_on_next_line = true
|
||||||
|
ij_javascript_binary_operation_wrap = on_every_item
|
||||||
|
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
|
||||||
|
ij_javascript_blank_lines_after_imports = 1
|
||||||
|
ij_javascript_blank_lines_around_class = 1
|
||||||
|
ij_javascript_blank_lines_around_field = 0
|
||||||
|
ij_javascript_blank_lines_around_function = 1
|
||||||
|
ij_javascript_blank_lines_around_method = 1
|
||||||
|
ij_javascript_block_brace_style = end_of_line
|
||||||
|
ij_javascript_call_parameters_new_line_after_left_paren = false
|
||||||
|
ij_javascript_call_parameters_right_paren_on_new_line = false
|
||||||
|
ij_javascript_call_parameters_wrap = on_every_item
|
||||||
|
ij_javascript_catch_on_new_line = false
|
||||||
|
ij_javascript_chained_call_dot_on_new_line = true
|
||||||
|
ij_javascript_class_brace_style = end_of_line
|
||||||
|
ij_javascript_comma_on_new_line = false
|
||||||
|
ij_javascript_do_while_brace_force = always
|
||||||
|
ij_javascript_else_on_new_line = false
|
||||||
|
ij_javascript_enforce_trailing_comma = remove
|
||||||
|
ij_javascript_extends_keyword_wrap = off
|
||||||
|
ij_javascript_extends_list_wrap = off
|
||||||
|
ij_javascript_field_prefix = _
|
||||||
|
ij_javascript_file_name_style = relaxed
|
||||||
|
ij_javascript_finally_on_new_line = false
|
||||||
|
ij_javascript_for_brace_force = always
|
||||||
|
ij_javascript_for_statement_new_line_after_left_paren = false
|
||||||
|
ij_javascript_for_statement_right_paren_on_new_line = false
|
||||||
|
ij_javascript_for_statement_wrap = off
|
||||||
|
ij_javascript_force_quote_style = true
|
||||||
|
ij_javascript_force_semicolon_style = true
|
||||||
|
ij_javascript_function_expression_brace_style = end_of_line
|
||||||
|
ij_javascript_if_brace_force = always
|
||||||
|
ij_javascript_import_merge_members = global
|
||||||
|
ij_javascript_import_prefer_absolute_path = global
|
||||||
|
ij_javascript_import_sort_members = true
|
||||||
|
ij_javascript_import_sort_module_name = false
|
||||||
|
ij_javascript_import_use_node_resolution = true
|
||||||
|
ij_javascript_imports_wrap = on_every_item
|
||||||
|
ij_javascript_indent_case_from_switch = true
|
||||||
|
ij_javascript_indent_chained_calls = true
|
||||||
|
ij_javascript_indent_package_children = 0
|
||||||
|
ij_javascript_jsx_attribute_value = braces
|
||||||
|
ij_javascript_keep_blank_lines_in_code = 1
|
||||||
|
ij_javascript_keep_first_column_comment = true
|
||||||
|
ij_javascript_keep_indents_on_empty_lines = false
|
||||||
|
ij_javascript_keep_line_breaks = true
|
||||||
|
ij_javascript_keep_simple_blocks_in_one_line = true
|
||||||
|
ij_javascript_keep_simple_methods_in_one_line = true
|
||||||
|
ij_javascript_line_comment_add_space = true
|
||||||
|
ij_javascript_line_comment_at_first_column = false
|
||||||
|
ij_javascript_method_brace_style = end_of_line
|
||||||
|
ij_javascript_method_call_chain_wrap = on_every_item
|
||||||
|
ij_javascript_method_parameters_new_line_after_left_paren = false
|
||||||
|
ij_javascript_method_parameters_right_paren_on_new_line = false
|
||||||
|
ij_javascript_method_parameters_wrap = on_every_item
|
||||||
|
ij_javascript_object_literal_wrap = on_every_item
|
||||||
|
ij_javascript_parentheses_expression_new_line_after_left_paren = false
|
||||||
|
ij_javascript_parentheses_expression_right_paren_on_new_line = false
|
||||||
|
ij_javascript_place_assignment_sign_on_next_line = false
|
||||||
|
ij_javascript_prefer_as_type_cast = false
|
||||||
|
ij_javascript_prefer_explicit_types_function_expression_returns = false
|
||||||
|
ij_javascript_prefer_explicit_types_function_returns = false
|
||||||
|
ij_javascript_prefer_explicit_types_vars_fields = false
|
||||||
|
ij_javascript_prefer_parameters_wrap = false
|
||||||
|
ij_javascript_reformat_c_style_comments = true
|
||||||
|
ij_javascript_space_after_colon = true
|
||||||
|
ij_javascript_space_after_comma = true
|
||||||
|
ij_javascript_space_after_dots_in_rest_parameter = false
|
||||||
|
ij_javascript_space_after_generator_mult = true
|
||||||
|
ij_javascript_space_after_property_colon = true
|
||||||
|
ij_javascript_space_after_quest = true
|
||||||
|
ij_javascript_space_after_type_colon = true
|
||||||
|
ij_javascript_space_after_unary_not = false
|
||||||
|
ij_javascript_space_before_async_arrow_lparen = true
|
||||||
|
ij_javascript_space_before_catch_keyword = true
|
||||||
|
ij_javascript_space_before_catch_left_brace = true
|
||||||
|
ij_javascript_space_before_catch_parentheses = true
|
||||||
|
ij_javascript_space_before_class_lbrace = true
|
||||||
|
ij_javascript_space_before_class_left_brace = true
|
||||||
|
ij_javascript_space_before_colon = true
|
||||||
|
ij_javascript_space_before_comma = false
|
||||||
|
ij_javascript_space_before_do_left_brace = true
|
||||||
|
ij_javascript_space_before_else_keyword = true
|
||||||
|
ij_javascript_space_before_else_left_brace = true
|
||||||
|
ij_javascript_space_before_finally_keyword = true
|
||||||
|
ij_javascript_space_before_finally_left_brace = true
|
||||||
|
ij_javascript_space_before_for_left_brace = true
|
||||||
|
ij_javascript_space_before_for_parentheses = true
|
||||||
|
ij_javascript_space_before_for_semicolon = false
|
||||||
|
ij_javascript_space_before_function_left_parenth = true
|
||||||
|
ij_javascript_space_before_generator_mult = false
|
||||||
|
ij_javascript_space_before_if_left_brace = true
|
||||||
|
ij_javascript_space_before_if_parentheses = true
|
||||||
|
ij_javascript_space_before_method_call_parentheses = false
|
||||||
|
ij_javascript_space_before_method_left_brace = true
|
||||||
|
ij_javascript_space_before_method_parentheses = false
|
||||||
|
ij_javascript_space_before_property_colon = false
|
||||||
|
ij_javascript_space_before_quest = true
|
||||||
|
ij_javascript_space_before_switch_left_brace = true
|
||||||
|
ij_javascript_space_before_switch_parentheses = true
|
||||||
|
ij_javascript_space_before_try_left_brace = true
|
||||||
|
ij_javascript_space_before_type_colon = false
|
||||||
|
ij_javascript_space_before_unary_not = false
|
||||||
|
ij_javascript_space_before_while_keyword = true
|
||||||
|
ij_javascript_space_before_while_left_brace = true
|
||||||
|
ij_javascript_space_before_while_parentheses = true
|
||||||
|
ij_javascript_spaces_around_additive_operators = true
|
||||||
|
ij_javascript_spaces_around_arrow_function_operator = true
|
||||||
|
ij_javascript_spaces_around_assignment_operators = true
|
||||||
|
ij_javascript_spaces_around_bitwise_operators = true
|
||||||
|
ij_javascript_spaces_around_equality_operators = true
|
||||||
|
ij_javascript_spaces_around_logical_operators = true
|
||||||
|
ij_javascript_spaces_around_multiplicative_operators = true
|
||||||
|
ij_javascript_spaces_around_relational_operators = true
|
||||||
|
ij_javascript_spaces_around_shift_operators = true
|
||||||
|
ij_javascript_spaces_around_unary_operator = false
|
||||||
|
ij_javascript_spaces_within_array_initializer_brackets = false
|
||||||
|
ij_javascript_spaces_within_brackets = false
|
||||||
|
ij_javascript_spaces_within_catch_parentheses = false
|
||||||
|
ij_javascript_spaces_within_for_parentheses = false
|
||||||
|
ij_javascript_spaces_within_if_parentheses = false
|
||||||
|
ij_javascript_spaces_within_imports = true
|
||||||
|
ij_javascript_spaces_within_interpolation_expressions = false
|
||||||
|
ij_javascript_spaces_within_method_call_parentheses = false
|
||||||
|
ij_javascript_spaces_within_method_parentheses = false
|
||||||
|
ij_javascript_spaces_within_object_literal_braces = true
|
||||||
|
ij_javascript_spaces_within_object_type_braces = true
|
||||||
|
ij_javascript_spaces_within_parentheses = false
|
||||||
|
ij_javascript_spaces_within_switch_parentheses = false
|
||||||
|
ij_javascript_spaces_within_type_assertion = false
|
||||||
|
ij_javascript_spaces_within_union_types = true
|
||||||
|
ij_javascript_spaces_within_while_parentheses = false
|
||||||
|
ij_javascript_special_else_if_treatment = true
|
||||||
|
ij_javascript_ternary_operation_signs_on_next_line = true
|
||||||
|
ij_javascript_ternary_operation_wrap = on_every_item
|
||||||
|
ij_javascript_union_types_wrap = on_every_item
|
||||||
|
ij_javascript_use_chained_calls_group_indents = false
|
||||||
|
ij_javascript_use_double_quotes = false
|
||||||
|
ij_javascript_use_explicit_js_extension = global
|
||||||
|
ij_javascript_use_path_mapping = always
|
||||||
|
ij_javascript_use_public_modifier = false
|
||||||
|
ij_javascript_use_semicolon_after_statement = true
|
||||||
|
ij_javascript_var_declaration_wrap = on_every_item
|
||||||
|
ij_javascript_while_brace_force = always
|
||||||
|
ij_javascript_while_on_new_line = false
|
||||||
|
ij_javascript_wrap_comments = false
|
||||||
|
|
||||||
|
[{*.cjsx,*.coffee}]
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_coffeescript_align_function_body = false
|
||||||
|
ij_coffeescript_align_imports = false
|
||||||
|
ij_coffeescript_align_multiline_array_initializer_expression = true
|
||||||
|
ij_coffeescript_align_multiline_parameters = true
|
||||||
|
ij_coffeescript_align_multiline_parameters_in_calls = false
|
||||||
|
ij_coffeescript_align_object_properties = 0
|
||||||
|
ij_coffeescript_align_union_types = false
|
||||||
|
ij_coffeescript_align_var_statements = 0
|
||||||
|
ij_coffeescript_array_initializer_new_line_after_left_brace = false
|
||||||
|
ij_coffeescript_array_initializer_right_brace_on_new_line = false
|
||||||
|
ij_coffeescript_array_initializer_wrap = normal
|
||||||
|
ij_coffeescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
|
||||||
|
ij_coffeescript_blank_lines_around_function = 1
|
||||||
|
ij_coffeescript_call_parameters_new_line_after_left_paren = false
|
||||||
|
ij_coffeescript_call_parameters_right_paren_on_new_line = false
|
||||||
|
ij_coffeescript_call_parameters_wrap = normal
|
||||||
|
ij_coffeescript_chained_call_dot_on_new_line = true
|
||||||
|
ij_coffeescript_comma_on_new_line = false
|
||||||
|
ij_coffeescript_enforce_trailing_comma = keep
|
||||||
|
ij_coffeescript_field_prefix = _
|
||||||
|
ij_coffeescript_file_name_style = relaxed
|
||||||
|
ij_coffeescript_force_quote_style = false
|
||||||
|
ij_coffeescript_force_semicolon_style = false
|
||||||
|
ij_coffeescript_function_expression_brace_style = end_of_line
|
||||||
|
ij_coffeescript_import_merge_members = global
|
||||||
|
ij_coffeescript_import_prefer_absolute_path = global
|
||||||
|
ij_coffeescript_import_sort_members = true
|
||||||
|
ij_coffeescript_import_sort_module_name = false
|
||||||
|
ij_coffeescript_import_use_node_resolution = true
|
||||||
|
ij_coffeescript_imports_wrap = on_every_item
|
||||||
|
ij_coffeescript_indent_chained_calls = true
|
||||||
|
ij_coffeescript_indent_package_children = 0
|
||||||
|
ij_coffeescript_jsx_attribute_value = braces
|
||||||
|
ij_coffeescript_keep_blank_lines_in_code = 2
|
||||||
|
ij_coffeescript_keep_first_column_comment = true
|
||||||
|
ij_coffeescript_keep_indents_on_empty_lines = false
|
||||||
|
ij_coffeescript_keep_line_breaks = true
|
||||||
|
ij_coffeescript_keep_simple_methods_in_one_line = false
|
||||||
|
ij_coffeescript_method_parameters_new_line_after_left_paren = false
|
||||||
|
ij_coffeescript_method_parameters_right_paren_on_new_line = false
|
||||||
|
ij_coffeescript_method_parameters_wrap = off
|
||||||
|
ij_coffeescript_object_literal_wrap = on_every_item
|
||||||
|
ij_coffeescript_prefer_as_type_cast = false
|
||||||
|
ij_coffeescript_prefer_explicit_types_function_expression_returns = false
|
||||||
|
ij_coffeescript_prefer_explicit_types_function_returns = false
|
||||||
|
ij_coffeescript_prefer_explicit_types_vars_fields = false
|
||||||
|
ij_coffeescript_reformat_c_style_comments = false
|
||||||
|
ij_coffeescript_space_after_comma = true
|
||||||
|
ij_coffeescript_space_after_dots_in_rest_parameter = false
|
||||||
|
ij_coffeescript_space_after_generator_mult = true
|
||||||
|
ij_coffeescript_space_after_property_colon = true
|
||||||
|
ij_coffeescript_space_after_type_colon = true
|
||||||
|
ij_coffeescript_space_after_unary_not = false
|
||||||
|
ij_coffeescript_space_before_async_arrow_lparen = true
|
||||||
|
ij_coffeescript_space_before_class_lbrace = true
|
||||||
|
ij_coffeescript_space_before_comma = false
|
||||||
|
ij_coffeescript_space_before_function_left_parenth = true
|
||||||
|
ij_coffeescript_space_before_generator_mult = false
|
||||||
|
ij_coffeescript_space_before_property_colon = false
|
||||||
|
ij_coffeescript_space_before_type_colon = false
|
||||||
|
ij_coffeescript_space_before_unary_not = false
|
||||||
|
ij_coffeescript_spaces_around_additive_operators = true
|
||||||
|
ij_coffeescript_spaces_around_arrow_function_operator = true
|
||||||
|
ij_coffeescript_spaces_around_assignment_operators = true
|
||||||
|
ij_coffeescript_spaces_around_bitwise_operators = true
|
||||||
|
ij_coffeescript_spaces_around_equality_operators = true
|
||||||
|
ij_coffeescript_spaces_around_logical_operators = true
|
||||||
|
ij_coffeescript_spaces_around_multiplicative_operators = true
|
||||||
|
ij_coffeescript_spaces_around_relational_operators = true
|
||||||
|
ij_coffeescript_spaces_around_shift_operators = true
|
||||||
|
ij_coffeescript_spaces_around_unary_operator = false
|
||||||
|
ij_coffeescript_spaces_within_array_initializer_braces = false
|
||||||
|
ij_coffeescript_spaces_within_array_initializer_brackets = false
|
||||||
|
ij_coffeescript_spaces_within_imports = false
|
||||||
|
ij_coffeescript_spaces_within_index_brackets = false
|
||||||
|
ij_coffeescript_spaces_within_interpolation_expressions = false
|
||||||
|
ij_coffeescript_spaces_within_method_call_parentheses = false
|
||||||
|
ij_coffeescript_spaces_within_method_parentheses = false
|
||||||
|
ij_coffeescript_spaces_within_object_braces = false
|
||||||
|
ij_coffeescript_spaces_within_object_literal_braces = false
|
||||||
|
ij_coffeescript_spaces_within_object_type_braces = true
|
||||||
|
ij_coffeescript_spaces_within_range_brackets = false
|
||||||
|
ij_coffeescript_spaces_within_type_assertion = false
|
||||||
|
ij_coffeescript_spaces_within_union_types = true
|
||||||
|
ij_coffeescript_union_types_wrap = on_every_item
|
||||||
|
ij_coffeescript_use_chained_calls_group_indents = false
|
||||||
|
ij_coffeescript_use_double_quotes = true
|
||||||
|
ij_coffeescript_use_explicit_js_extension = global
|
||||||
|
ij_coffeescript_use_path_mapping = always
|
||||||
|
ij_coffeescript_use_public_modifier = false
|
||||||
|
ij_coffeescript_use_semicolon_after_statement = false
|
||||||
|
ij_coffeescript_var_declaration_wrap = normal
|
||||||
|
|
||||||
|
[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}]
|
||||||
|
tab_width = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_json_keep_blank_lines_in_code = 0
|
||||||
|
ij_json_keep_indents_on_empty_lines = false
|
||||||
|
ij_json_keep_line_breaks = true
|
||||||
|
ij_json_space_after_colon = true
|
||||||
|
ij_json_space_after_comma = true
|
||||||
|
ij_json_space_before_colon = false
|
||||||
|
ij_json_space_before_comma = false
|
||||||
|
ij_json_spaces_within_braces = false
|
||||||
|
ij_json_spaces_within_brackets = false
|
||||||
|
ij_json_wrap_long_lines = false
|
||||||
|
|
||||||
|
[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
||||||
|
ij_html_align_attributes = true
|
||||||
|
ij_html_align_text = false
|
||||||
|
ij_html_attribute_wrap = on_every_item
|
||||||
|
ij_html_block_comment_at_first_column = true
|
||||||
|
ij_html_do_not_align_children_of_min_lines = 0
|
||||||
|
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
|
||||||
|
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
|
||||||
|
ij_html_enforce_quotes = false
|
||||||
|
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
|
||||||
|
ij_html_keep_blank_lines = 1
|
||||||
|
ij_html_keep_indents_on_empty_lines = false
|
||||||
|
ij_html_keep_line_breaks = true
|
||||||
|
ij_html_keep_line_breaks_in_text = true
|
||||||
|
ij_html_keep_whitespaces = false
|
||||||
|
ij_html_keep_whitespaces_inside = span,pre,textarea
|
||||||
|
ij_html_line_comment_at_first_column = true
|
||||||
|
ij_html_new_line_after_last_attribute = when multiline
|
||||||
|
ij_html_new_line_before_first_attribute = when multiline
|
||||||
|
ij_html_quote_style = double
|
||||||
|
ij_html_remove_new_line_before_tags = br
|
||||||
|
ij_html_space_after_tag_name = false
|
||||||
|
ij_html_space_around_equality_in_attribute = false
|
||||||
|
ij_html_space_inside_empty_tag = true
|
||||||
|
ij_html_text_wrap = off
|
||||||
|
ij_html_uniform_ident = false
|
||||||
|
|
||||||
|
[{*.jade,*.pug}]
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
ij_visual_guides = none
|
||||||
|
|
||||||
|
[{*.markdown,*.md}]
|
||||||
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_continuation_indent_size = 2
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_markdown_force_one_space_after_blockquote_symbol = true
|
||||||
|
ij_markdown_force_one_space_after_header_symbol = true
|
||||||
|
ij_markdown_force_one_space_after_list_bullet = true
|
||||||
|
ij_markdown_force_one_space_between_words = true
|
||||||
|
ij_markdown_keep_indents_on_empty_lines = false
|
||||||
|
ij_markdown_max_lines_around_block_elements = 1
|
||||||
|
ij_markdown_max_lines_around_header = 1
|
||||||
|
ij_markdown_max_lines_between_paragraphs = 1
|
||||||
|
ij_markdown_min_lines_around_block_elements = 1
|
||||||
|
ij_markdown_min_lines_around_header = 1
|
||||||
|
ij_markdown_min_lines_between_paragraphs = 1
|
||||||
|
|
||||||
|
[{*.yaml,*.yml}]
|
||||||
|
ij_visual_guides = none
|
||||||
|
ij_yaml_align_values_properties = do_not_align
|
||||||
|
ij_yaml_autoinsert_sequence_marker = true
|
||||||
|
ij_yaml_block_mapping_on_new_line = false
|
||||||
|
ij_yaml_indent_sequence_value = true
|
||||||
|
ij_yaml_keep_indents_on_empty_lines = false
|
||||||
|
ij_yaml_keep_line_breaks = true
|
||||||
|
ij_yaml_sequence_on_new_line = false
|
||||||
|
ij_yaml_space_before_colon = false
|
||||||
|
ij_yaml_spaces_within_braces = true
|
||||||
|
ij_yaml_spaces_within_brackets = true
|
||||||
14
frontend/.eslintrc.json
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals",
|
||||||
|
"rules": {
|
||||||
|
"@next/next/no-img-element": "off",
|
||||||
|
"jsx-a11y/alt-text": "off",
|
||||||
|
"react/display-name": "off",
|
||||||
|
"react/jsx-max-props-per-line": [
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
"maximum": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
1
frontend/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* text=lf
|
||||||
28
frontend/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
out
|
||||||
|
.next
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.eslintcache
|
||||||
|
.idea
|
||||||
|
/.env
|
||||||
|
/.env.local
|
||||||
|
/.env.development.local
|
||||||
|
/.env.test.local
|
||||||
|
/.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
109
frontend/README.md
Executable file
|
|
@ -0,0 +1,109 @@
|
||||||
|
## [Material Kit - React](https://material-kit-react.devias.io/) [](https://twitter.com/intent/tweet?text=%F0%9F%9A%A8Devias%20Freebie%20Alert%20-%20An%20awesome%20ready-to-use%20register%20page%20made%20with%20%23material%20%23react%0D%0Ahttps%3A%2F%2Fdevias.io%20%23createreactapp%20%23devias%20%23material%20%23freebie%20%40devias-io)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
[](https://material-kit-react.devias.io/)
|
||||||
|
|
||||||
|
> Free React Admin Dashboard made with [MUI's](https://mui.com/?ref=devias-io)
|
||||||
|
> components, [React](https://reactjs.org/?ref=devias-io) and of
|
||||||
|
> course [Next.js](https://github.com/vercel/next.js/?ref=devias-io) to boost your app development
|
||||||
|
> process!
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
- [Dashboard Page](https://material-kit-react.devias.io)
|
||||||
|
- [Companies Page](https://material-kit-react.devias.io/companies)
|
||||||
|
- [Customers Page](https://material-kit-react.devias.io/customers)
|
||||||
|
- [Account Page](https://material-kit-react.devias.io/account)
|
||||||
|
- [Settings Page](https://material-kit-react.devias.io/settings)
|
||||||
|
- [Login Page](https://material-kit-react.devias.io/auth/login)
|
||||||
|
- [Register Page](https://material-kit-react.devias.io/auth/register)
|
||||||
|
|
||||||
|
## Free Figma Community File
|
||||||
|
|
||||||
|
- [Duplicate File](https://www.figma.com/community/file/1039837897183395483/Devias-Dashboard-Design-Library-Kit)
|
||||||
|
|
||||||
|
## Upgrade to PRO Version
|
||||||
|
|
||||||
|
We also have a pro version of this product which bundles even more pages and components if you want
|
||||||
|
to save more time and design efforts :)
|
||||||
|
|
||||||
|
| Free Version (this one) | [Material Kit Pro - React](https://mui.com/store/items/devias-kit-pro/) |
|
||||||
|
|-------------------------|:-------------------------------------------------------------------------|
|
||||||
|
| **9** Demo Pages | **40+** demo pages
|
||||||
|
| ✔ Mocked Authentication | ✔ Authentication with **Amplify**, **Auth0**, **JWT** and **Firebase**
|
||||||
|
| - | ✔ Dark & light mode
|
||||||
|
| - | ✔ CRA version
|
||||||
|
| - | ✔ TypeScript version - for Standard Plus and Extended license
|
||||||
|
| - | ✔ Design files (sketch & figma) - for Standard Plus and Extended license
|
||||||
|
| - | ✔ Complete users flows
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
- [Download from Github](https://github.com/devias-io/material-kit-react/archive/master.zip)
|
||||||
|
or [Download from Devias](https://devias.io/products/material-kit-react) or clone the
|
||||||
|
repo: `git clone https://github.com/devias-io/material-kit-react.git`
|
||||||
|
|
||||||
|
- Make sure your Node.js and npm versions are up to date for `React 18`
|
||||||
|
|
||||||
|
- Install dependencies: `npm install` or `yarn`
|
||||||
|
|
||||||
|
- Start the server: `npm run dev` or `yarn dev`
|
||||||
|
|
||||||
|
- Views are on: `localhost:3000`
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
Within the download you'll find the following directories and files:
|
||||||
|
|
||||||
|
```
|
||||||
|
material-kit-react
|
||||||
|
|
||||||
|
┌── .eslintrc.json
|
||||||
|
├── .gitignore
|
||||||
|
├── CHANGELOG.md
|
||||||
|
├── LICENSE.md
|
||||||
|
├── next.config.js
|
||||||
|
├── package.json
|
||||||
|
├── README.md
|
||||||
|
├── public
|
||||||
|
└── src
|
||||||
|
├── components
|
||||||
|
├── contexts
|
||||||
|
├── guards
|
||||||
|
├── hocs
|
||||||
|
├── hooks
|
||||||
|
├── layouts
|
||||||
|
├── sections
|
||||||
|
├── theme
|
||||||
|
├── utils
|
||||||
|
└── pages
|
||||||
|
├── 404.js
|
||||||
|
├── _app.js
|
||||||
|
├── _document.js
|
||||||
|
├── account.js
|
||||||
|
├── companies.js
|
||||||
|
├── customers.js
|
||||||
|
├── index.js
|
||||||
|
├── products.js
|
||||||
|
└── settings.js
|
||||||
|
└── auth
|
||||||
|
├── login.js
|
||||||
|
└── register.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- More freebies like this one: <https://devias.io>
|
||||||
|
|
||||||
|
## Reporting Issues:
|
||||||
|
|
||||||
|
- [Github Issues Page](https://github.com/devias-io/react-material-dashboard/issues?ref=devias-io)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
- Licensed under MIT (https://github.com/devias-io/react-material-dashboard/blob/master/LICENSE.md)
|
||||||
|
|
||||||
|
## Contact Us
|
||||||
|
|
||||||
|
- Email Us: support@deviasio.zendesk.com
|
||||||
5
frontend/jsconfig.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "."
|
||||||
|
}
|
||||||
|
}
|
||||||
3
frontend/next.config.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
reactStrictMode: true
|
||||||
|
};
|
||||||
8204
frontend/package-lock.json
generated
Normal file
47
frontend/package.json
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"name": "material-kit-react",
|
||||||
|
"version": "3.0.0",
|
||||||
|
"author": "Devias",
|
||||||
|
"licence": "MIT",
|
||||||
|
"homepage": "https://devias.io",
|
||||||
|
"private": false,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"export": "next export",
|
||||||
|
"lint": "next lint",
|
||||||
|
"lint-fix": "next lint --fix"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/cache": "11.10.5",
|
||||||
|
"@emotion/react": "11.10.6",
|
||||||
|
"@emotion/server": "11.10.0",
|
||||||
|
"@emotion/styled": "11.10.6",
|
||||||
|
"@heroicons/react": "2.0.16",
|
||||||
|
"@mui/lab": "5.0.0-alpha.120",
|
||||||
|
"@mui/material": "5.11.10",
|
||||||
|
"@mui/system": "5.11.9",
|
||||||
|
"@mui/x-date-pickers": "5.0.19",
|
||||||
|
"apexcharts": "3.37.0",
|
||||||
|
"date-fns": "2.29.3",
|
||||||
|
"formik": "2.2.9",
|
||||||
|
"next": "13.1.6",
|
||||||
|
"nprogress": "0.2.0",
|
||||||
|
"prop-types": "15.8.1",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-apexcharts": "1.4.0",
|
||||||
|
"react-dom": "18.2.0",
|
||||||
|
"simplebar-react": "^3.2.1",
|
||||||
|
"yup": "1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "18.13.0",
|
||||||
|
"@types/nprogress": "0.2.0",
|
||||||
|
"@types/numeral": "2.0.2",
|
||||||
|
"@types/react": "18.0.28",
|
||||||
|
"@types/react-dom": "18.0.11",
|
||||||
|
"eslint": "8.34.0",
|
||||||
|
"eslint-config-next": "13.1.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
94
frontend/public/assets/auth-illustration.svg
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
frontend/public/assets/avatars/avatar-alcides-antonio.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
frontend/public/assets/avatars/avatar-anika-visser.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
frontend/public/assets/avatars/avatar-cao-yu.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
frontend/public/assets/avatars/avatar-carson-darrin.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
frontend/public/assets/avatars/avatar-chinasa-neo.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
frontend/public/assets/avatars/avatar-fran-perez.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
frontend/public/assets/avatars/avatar-iulia-albu.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
frontend/public/assets/avatars/avatar-jane-rotanson.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
frontend/public/assets/avatars/avatar-jie-yan-song.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
frontend/public/assets/avatars/avatar-marcus-finn.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
frontend/public/assets/avatars/avatar-miron-vitold.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
frontend/public/assets/avatars/avatar-nasimiyu-danai.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
frontend/public/assets/avatars/avatar-neha-punita.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
frontend/public/assets/avatars/avatar-omar-darboe.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
frontend/public/assets/avatars/avatar-penjani-inyene.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
frontend/public/assets/avatars/avatar-seo-hyeon-ji.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
frontend/public/assets/avatars/avatar-siegbert-gottfried.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
frontend/public/assets/devias-kit-pro.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
frontend/public/assets/errors/error-401.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
frontend/public/assets/errors/error-404.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
frontend/public/assets/errors/error-500.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
frontend/public/assets/logos/logo-dropbox.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/public/assets/logos/logo-github.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
frontend/public/assets/logos/logo-lyft.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
frontend/public/assets/logos/logo-medium.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
frontend/public/assets/logos/logo-slack.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
frontend/public/assets/logos/logo-squarespace.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
frontend/public/assets/products/product-1.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
frontend/public/assets/products/product-2.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
frontend/public/assets/products/product-4.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
frontend/public/assets/products/product-5.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
frontend/public/assets/products/product-6.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
frontend/public/assets/products/product-7.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
frontend/public/assets/thumbnail.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
frontend/public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 777 B |
BIN
frontend/public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
frontend/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
15
frontend/public/manifest.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"short_name": "Devias Kit",
|
||||||
|
"name": "Devias Kit",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
9
frontend/src/components/chart.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
|
||||||
|
const ApexChart = dynamic(() => import('react-apexcharts'), {
|
||||||
|
ssr: false,
|
||||||
|
loading: () => null
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Chart = styled(ApexChart)``;
|
||||||
26
frontend/src/components/logo.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
|
||||||
|
export const Logo = () => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const fillColor = theme.palette.primary.main;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
fill="none"
|
||||||
|
height="100%"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="100%"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
opacity={0.16}
|
||||||
|
d="M7.242 11.083c.449-1.674 2.17-3.394 3.843-3.843l10.434-2.796c1.673-.448 2.666.545 2.218 2.218L20.94 17.096c-.449 1.674-2.17 3.394-3.843 3.843L6.664 23.735c-1.673.448-2.666-.545-2.218-2.218l2.796-10.434Z"
|
||||||
|
fill={fillColor}
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M3.06 6.9c.448-1.674 2.168-3.394 3.842-3.843L17.336.261c1.673-.448 2.667.545 2.218 2.218l-2.796 10.434c-.449 1.674-2.169 3.394-3.843 3.843L2.481 19.552C.808 20-.185 19.007.263 17.334L3.06 6.9Z"
|
||||||
|
fill={fillColor}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
4
frontend/src/components/scrollbar.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import SimpleBar from 'simplebar-react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
|
||||||
|
export const Scrollbar = styled(SimpleBar)``;
|
||||||
58
frontend/src/components/severity-pill.js
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
|
||||||
|
const SeverityPillRoot = styled('span')(({ theme, ownerState }) => {
|
||||||
|
const backgroundColor = theme.palette[ownerState.color].alpha12;
|
||||||
|
const color = theme.palette.mode === 'dark'
|
||||||
|
? theme.palette[ownerState.color].main
|
||||||
|
: theme.palette[ownerState.color].dark;
|
||||||
|
|
||||||
|
return {
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor,
|
||||||
|
borderRadius: 12,
|
||||||
|
color,
|
||||||
|
cursor: 'default',
|
||||||
|
display: 'inline-flex',
|
||||||
|
flexGrow: 0,
|
||||||
|
flexShrink: 0,
|
||||||
|
fontFamily: theme.typography.fontFamily,
|
||||||
|
fontSize: theme.typography.pxToRem(12),
|
||||||
|
lineHeight: 2,
|
||||||
|
fontWeight: 600,
|
||||||
|
justifyContent: 'center',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
minWidth: 20,
|
||||||
|
paddingLeft: theme.spacing(1),
|
||||||
|
paddingRight: theme.spacing(1),
|
||||||
|
textTransform: 'uppercase',
|
||||||
|
whiteSpace: 'nowrap'
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SeverityPill = (props) => {
|
||||||
|
const { color = 'primary', children, ...other } = props;
|
||||||
|
|
||||||
|
const ownerState = { color };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SeverityPillRoot
|
||||||
|
ownerState={ownerState}
|
||||||
|
{...other}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SeverityPillRoot>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SeverityPill.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
color: PropTypes.oneOf([
|
||||||
|
'primary',
|
||||||
|
'secondary',
|
||||||
|
'error',
|
||||||
|
'info',
|
||||||
|
'warning',
|
||||||
|
'success'
|
||||||
|
])
|
||||||
|
};
|
||||||
185
frontend/src/contexts/auth-context.js
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
import { createContext, useContext, useEffect, useReducer, useRef } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const HANDLERS = {
|
||||||
|
INITIALIZE: 'INITIALIZE',
|
||||||
|
SIGN_IN: 'SIGN_IN',
|
||||||
|
SIGN_OUT: 'SIGN_OUT'
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
isAuthenticated: false,
|
||||||
|
isLoading: true,
|
||||||
|
user: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlers = {
|
||||||
|
[HANDLERS.INITIALIZE]: (state, action) => {
|
||||||
|
const user = action.payload;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...(
|
||||||
|
// if payload (user) is provided, then is authenticated
|
||||||
|
user
|
||||||
|
? ({
|
||||||
|
isAuthenticated: true,
|
||||||
|
isLoading: false,
|
||||||
|
user
|
||||||
|
})
|
||||||
|
: ({
|
||||||
|
isLoading: false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[HANDLERS.SIGN_IN]: (state, action) => {
|
||||||
|
const user = action.payload;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isAuthenticated: true,
|
||||||
|
user
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[HANDLERS.SIGN_OUT]: (state) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isAuthenticated: false,
|
||||||
|
user: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const reducer = (state, action) => (
|
||||||
|
handlers[action.type] ? handlers[action.type](state, action) : state
|
||||||
|
);
|
||||||
|
|
||||||
|
// The role of this context is to propagate authentication state through the App tree.
|
||||||
|
|
||||||
|
export const AuthContext = createContext({ undefined });
|
||||||
|
|
||||||
|
export const AuthProvider = (props) => {
|
||||||
|
const { children } = props;
|
||||||
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
|
const initialized = useRef(false);
|
||||||
|
|
||||||
|
const initialize = async () => {
|
||||||
|
// Prevent from calling twice in development mode with React.StrictMode enabled
|
||||||
|
if (initialized.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized.current = true;
|
||||||
|
|
||||||
|
let isAuthenticated = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
isAuthenticated = window.sessionStorage.getItem('authenticated') === 'true';
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAuthenticated) {
|
||||||
|
const user = {
|
||||||
|
id: '5e86809283e28b96d2d38537',
|
||||||
|
avatar: '/assets/avatars/avatar-anika-visser.png',
|
||||||
|
name: 'Anika Visser',
|
||||||
|
email: 'anika.visser@devias.io'
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: HANDLERS.INITIALIZE,
|
||||||
|
payload: user
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: HANDLERS.INITIALIZE
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
initialize();
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const skip = () => {
|
||||||
|
try {
|
||||||
|
window.sessionStorage.setItem('authenticated', 'true');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
id: '5e86809283e28b96d2d38537',
|
||||||
|
avatar: '/assets/avatars/avatar-anika-visser.png',
|
||||||
|
name: 'Anika Visser',
|
||||||
|
email: 'anika.visser@devias.io'
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: HANDLERS.SIGN_IN,
|
||||||
|
payload: user
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const signIn = async (email, password) => {
|
||||||
|
if (email !== 'demo@devias.io' || password !== 'Password123!') {
|
||||||
|
throw new Error('Please check your email and password');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
window.sessionStorage.setItem('authenticated', 'true');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
id: '5e86809283e28b96d2d38537',
|
||||||
|
avatar: '/assets/avatars/avatar-anika-visser.png',
|
||||||
|
name: 'Anika Visser',
|
||||||
|
email: 'anika.visser@devias.io'
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: HANDLERS.SIGN_IN,
|
||||||
|
payload: user
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const signUp = async (email, name, password) => {
|
||||||
|
throw new Error('Sign up is not implemented');
|
||||||
|
};
|
||||||
|
|
||||||
|
const signOut = () => {
|
||||||
|
dispatch({
|
||||||
|
type: HANDLERS.SIGN_OUT
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthContext.Provider
|
||||||
|
value={{
|
||||||
|
...state,
|
||||||
|
skip,
|
||||||
|
signIn,
|
||||||
|
signUp,
|
||||||
|
signOut
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AuthContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthProvider.propTypes = {
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AuthConsumer = AuthContext.Consumer;
|
||||||
|
|
||||||
|
export const useAuthContext = () => useContext(AuthContext);
|
||||||
57
frontend/src/guards/auth-guard.js
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useAuthContext } from 'src/contexts/auth-context';
|
||||||
|
|
||||||
|
export const AuthGuard = (props) => {
|
||||||
|
const { children } = props;
|
||||||
|
const router = useRouter();
|
||||||
|
const { isAuthenticated } = useAuthContext();
|
||||||
|
const ignore = useRef(false);
|
||||||
|
const [checked, setChecked] = useState(false);
|
||||||
|
|
||||||
|
// Only do authentication check on component mount.
|
||||||
|
// This flow allows you to manually redirect the user after sign-out, otherwise this will be
|
||||||
|
// triggered and will automatically redirect to sign-in page.
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
if (!router.isReady) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent from calling twice in development mode with React.StrictMode enabled
|
||||||
|
if (ignore.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ignore.current = true;
|
||||||
|
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
console.log('Not authenticated, redirecting');
|
||||||
|
router
|
||||||
|
.replace({
|
||||||
|
pathname: '/auth/login',
|
||||||
|
query: router.asPath !== '/' ? { continueUrl: router.asPath } : undefined
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
} else {
|
||||||
|
setChecked(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[router.isReady]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!checked) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If got here, it means that the redirect did not occur, and that tells us that the user is
|
||||||
|
// authenticated / authorized.
|
||||||
|
|
||||||
|
return children;
|
||||||
|
};
|
||||||
|
|
||||||
|
AuthGuard.propTypes = {
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
7
frontend/src/hocs/with-auth-guard.js
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { AuthGuard } from 'src/guards/auth-guard';
|
||||||
|
|
||||||
|
export const withAuthGuard = (Component) => (props) => (
|
||||||
|
<AuthGuard>
|
||||||
|
<Component {...props} />
|
||||||
|
</AuthGuard>
|
||||||
|
);
|
||||||
4
frontend/src/hooks/use-auth.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import { AuthContext } from 'src/contexts/auth-context';
|
||||||
|
|
||||||
|
export const useAuth = () => useContext(AuthContext);
|
||||||
10
frontend/src/hooks/use-mocked-user.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
export const useMockedUser = () => {
|
||||||
|
// To get the user from the authContext, you can use
|
||||||
|
// `const { user } = useAuth();`
|
||||||
|
return {
|
||||||
|
id: '5e86809283e28b96d2d38537',
|
||||||
|
avatar: '/assets/avatars/avatar-anika-visser.png',
|
||||||
|
name: 'Anika Visser',
|
||||||
|
email: 'anika.visser@devias.io'
|
||||||
|
};
|
||||||
|
};
|
||||||
17
frontend/src/hooks/use-nprogress.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import Router from 'next/router';
|
||||||
|
import nProgress from 'nprogress';
|
||||||
|
|
||||||
|
export function useNProgress() {
|
||||||
|
useEffect(() => {
|
||||||
|
Router.events.on('routeChangeStart', nProgress.start);
|
||||||
|
Router.events.on('routeChangeError', nProgress.done);
|
||||||
|
Router.events.on('routeChangeComplete', nProgress.done);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
Router.events.off('routeChangeStart', nProgress.start);
|
||||||
|
Router.events.off('routeChangeError', nProgress.done);
|
||||||
|
Router.events.off('routeChangeComplete', nProgress.done);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
26
frontend/src/hooks/use-popover.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { useCallback, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
export function usePopover() {
|
||||||
|
const anchorRef = useRef(null);
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleOpen = useCallback(() => {
|
||||||
|
setOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleClose = useCallback(() => {
|
||||||
|
setOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleToggle = useCallback(() => {
|
||||||
|
setOpen((prevState) => !prevState);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
anchorRef,
|
||||||
|
handleClose,
|
||||||
|
handleOpen,
|
||||||
|
handleToggle,
|
||||||
|
open
|
||||||
|
};
|
||||||
|
}
|
||||||
35
frontend/src/hooks/use-selection.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export const useSelection = (items = []) => {
|
||||||
|
const [selected, setSelected] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelected([]);
|
||||||
|
}, [items]);
|
||||||
|
|
||||||
|
const handleSelectAll = useCallback(() => {
|
||||||
|
setSelected([...items]);
|
||||||
|
}, [items]);
|
||||||
|
|
||||||
|
const handleSelectOne = useCallback((item) => {
|
||||||
|
setSelected((prevState) => [...prevState, item]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDeselectAll = useCallback(() => {
|
||||||
|
setSelected([]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDeselectOne = useCallback((item) => {
|
||||||
|
setSelected((prevState) => {
|
||||||
|
return prevState.filter((_item) => _item !== item);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleDeselectAll,
|
||||||
|
handleDeselectOne,
|
||||||
|
handleSelectAll,
|
||||||
|
handleSelectOne,
|
||||||
|
selected
|
||||||
|
};
|
||||||
|
};
|
||||||
111
frontend/src/layouts/auth/layout.js
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import NextLink from 'next/link';
|
||||||
|
import { Box, Typography, Unstable_Grid2 as Grid } from '@mui/material';
|
||||||
|
import { Logo } from 'src/components/logo';
|
||||||
|
|
||||||
|
// TODO: Change subtitle text
|
||||||
|
|
||||||
|
export const Layout = (props) => {
|
||||||
|
const { children } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flex: '1 1 auto'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
sx={{ flex: '1 1 auto' }}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
lg={6}
|
||||||
|
sx={{
|
||||||
|
backgroundColor: 'background.paper',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
position: 'relative'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
component="header"
|
||||||
|
sx={{
|
||||||
|
left: 0,
|
||||||
|
p: 3,
|
||||||
|
position: 'fixed',
|
||||||
|
top: 0,
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
component={NextLink}
|
||||||
|
href="/"
|
||||||
|
sx={{
|
||||||
|
display: 'inline-flex',
|
||||||
|
height: 32,
|
||||||
|
width: 32
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Logo />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
{children}
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
lg={6}
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
background: 'radial-gradient(50% 50% at 50% 50%, #122647 0%, #090E23 100%)',
|
||||||
|
color: 'white',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
'& img': {
|
||||||
|
maxWidth: '100%'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ p: 3 }}>
|
||||||
|
<Typography
|
||||||
|
align="center"
|
||||||
|
color="inherit"
|
||||||
|
sx={{
|
||||||
|
fontSize: '24px',
|
||||||
|
lineHeight: '32px',
|
||||||
|
mb: 1
|
||||||
|
}}
|
||||||
|
variant="h1"
|
||||||
|
>
|
||||||
|
Welcome to{' '}
|
||||||
|
<Box
|
||||||
|
component="a"
|
||||||
|
sx={{ color: '#15B79E' }}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Devias Kit
|
||||||
|
</Box>
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
align="center"
|
||||||
|
sx={{ mb: 3 }}
|
||||||
|
variant="subtitle1"
|
||||||
|
>
|
||||||
|
A professional kit that comes with ready-to-use MUI components.
|
||||||
|
</Typography>
|
||||||
|
<img
|
||||||
|
alt=""
|
||||||
|
src="/assets/auth-illustration.svg"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Layout.prototypes = {
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
71
frontend/src/layouts/dashboard/account-popover.js
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Box, Divider, MenuItem, MenuList, Popover, Typography } from '@mui/material';
|
||||||
|
import { useAuth } from 'src/hooks/use-auth';
|
||||||
|
|
||||||
|
export const AccountPopover = (props) => {
|
||||||
|
const { anchorEl, onClose, open } = props;
|
||||||
|
const router = useRouter();
|
||||||
|
const auth = useAuth();
|
||||||
|
|
||||||
|
const handleSignOut = useCallback(
|
||||||
|
() => {
|
||||||
|
onClose?.();
|
||||||
|
auth.signOut();
|
||||||
|
router.push('/auth/login');
|
||||||
|
},
|
||||||
|
[onClose, auth, router]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
anchorOrigin={{
|
||||||
|
horizontal: 'left',
|
||||||
|
vertical: 'bottom'
|
||||||
|
}}
|
||||||
|
onClose={onClose}
|
||||||
|
open={open}
|
||||||
|
PaperProps={{ sx: { width: 200 } }}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
py: 1.5,
|
||||||
|
px: 2
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="overline">
|
||||||
|
Account
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
color="text.secondary"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
Anika Visser
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Divider />
|
||||||
|
<MenuList
|
||||||
|
disablePadding
|
||||||
|
dense
|
||||||
|
sx={{
|
||||||
|
p: '8px',
|
||||||
|
'& > *': {
|
||||||
|
borderRadius: 1
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem onClick={handleSignOut}>
|
||||||
|
Sign out
|
||||||
|
</MenuItem>
|
||||||
|
</MenuList>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
AccountPopover.propTypes = {
|
||||||
|
anchorEl: PropTypes.any,
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
open: PropTypes.bool.isRequired
|
||||||
|
};
|
||||||
84
frontend/src/layouts/dashboard/config.js
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import ChartBarIcon from '@heroicons/react/24/solid/ChartBarIcon';
|
||||||
|
import CogIcon from '@heroicons/react/24/solid/CogIcon';
|
||||||
|
import LockClosedIcon from '@heroicons/react/24/solid/LockClosedIcon';
|
||||||
|
import ShoppingBagIcon from '@heroicons/react/24/solid/ShoppingBagIcon';
|
||||||
|
import UserIcon from '@heroicons/react/24/solid/UserIcon';
|
||||||
|
import UserPlusIcon from '@heroicons/react/24/solid/UserPlusIcon';
|
||||||
|
import UsersIcon from '@heroicons/react/24/solid/UsersIcon';
|
||||||
|
import XCircleIcon from '@heroicons/react/24/solid/XCircleIcon';
|
||||||
|
import { SvgIcon } from '@mui/material';
|
||||||
|
|
||||||
|
export const items = [
|
||||||
|
{
|
||||||
|
title: 'Overview',
|
||||||
|
path: '/',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ChartBarIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Customers',
|
||||||
|
path: '/customers',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<UsersIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Companies',
|
||||||
|
path: '/companies',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ShoppingBagIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Account',
|
||||||
|
path: '/account',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<UserIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Settings',
|
||||||
|
path: '/settings',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<CogIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Login',
|
||||||
|
path: '/auth/login',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<LockClosedIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Register',
|
||||||
|
path: '/auth/register',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<UserPlusIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Error',
|
||||||
|
path: '/404',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<XCircleIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
54
frontend/src/layouts/dashboard/layout.js
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import { withAuthGuard } from 'src/hocs/with-auth-guard';
|
||||||
|
import { SideNav } from './side-nav';
|
||||||
|
import { TopNav } from './top-nav';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
|
||||||
|
const SIDE_NAV_WIDTH = 280;
|
||||||
|
|
||||||
|
const LayoutRoot = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flex: '1 1 auto',
|
||||||
|
maxWidth: '100%',
|
||||||
|
[theme.breakpoints.up('lg')]: {
|
||||||
|
paddingLeft: SIDE_NAV_WIDTH
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const LayoutContainer = styled('div')({
|
||||||
|
display: 'flex',
|
||||||
|
flex: '1 1 auto',
|
||||||
|
flexDirection: 'column',
|
||||||
|
width: '100%'
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Layout = withAuthGuard((props) => {
|
||||||
|
const { children } = props;
|
||||||
|
const pathname = usePathname();
|
||||||
|
const [openNav, setOpenNav] = useState(true);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
if (openNav) {
|
||||||
|
setOpenNav(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[pathname, openNav]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TopNav onNavOpen={() => setOpenNav(true)} />
|
||||||
|
<SideNav
|
||||||
|
onClose={() => setOpenNav(false)}
|
||||||
|
open={openNav}
|
||||||
|
/>
|
||||||
|
<LayoutRoot>
|
||||||
|
<LayoutContainer>
|
||||||
|
{children}
|
||||||
|
</LayoutContainer>
|
||||||
|
</LayoutRoot>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
92
frontend/src/layouts/dashboard/side-nav-item.js
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
import NextLink from 'next/link';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Box, ButtonBase } from '@mui/material';
|
||||||
|
|
||||||
|
export const SideNavItem = (props) => {
|
||||||
|
const { active = false, disabled, external, icon, path, title } = props;
|
||||||
|
|
||||||
|
const linkProps = path
|
||||||
|
? external
|
||||||
|
? {
|
||||||
|
component: 'a',
|
||||||
|
href: path,
|
||||||
|
target: '_blank'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
component: NextLink,
|
||||||
|
href: path
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<ButtonBase
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 1,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
pl: '16px',
|
||||||
|
pr: '16px',
|
||||||
|
py: '6px',
|
||||||
|
textAlign: 'left',
|
||||||
|
width: '100%',
|
||||||
|
...(active && {
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.04)'
|
||||||
|
}),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.04)'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{...linkProps}
|
||||||
|
>
|
||||||
|
{icon && (
|
||||||
|
<Box
|
||||||
|
component="span"
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
color: 'neutral.400',
|
||||||
|
display: 'inline-flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
mr: 2,
|
||||||
|
...(active && {
|
||||||
|
color: 'primary.main'
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Box
|
||||||
|
component="span"
|
||||||
|
sx={{
|
||||||
|
color: 'neutral.400',
|
||||||
|
flexGrow: 1,
|
||||||
|
fontFamily: (theme) => theme.typography.fontFamily,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: '24px',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
...(active && {
|
||||||
|
color: 'common.white'
|
||||||
|
}),
|
||||||
|
...(disabled && {
|
||||||
|
color: 'neutral.500'
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</Box>
|
||||||
|
</ButtonBase>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SideNavItem.propTypes = {
|
||||||
|
active: PropTypes.bool,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
external: PropTypes.bool,
|
||||||
|
icon: PropTypes.node,
|
||||||
|
path: PropTypes.string,
|
||||||
|
title: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
223
frontend/src/layouts/dashboard/side-nav.js
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
import NextLink from 'next/link';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ArrowTopRightOnSquareIcon from '@heroicons/react/24/solid/ArrowTopRightOnSquareIcon';
|
||||||
|
import ChevronUpDownIcon from '@heroicons/react/24/solid/ChevronUpDownIcon';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
Drawer,
|
||||||
|
Stack,
|
||||||
|
SvgIcon,
|
||||||
|
Typography,
|
||||||
|
useMediaQuery
|
||||||
|
} from '@mui/material';
|
||||||
|
import { Logo } from 'src/components/logo';
|
||||||
|
import { Scrollbar } from 'src/components/scrollbar';
|
||||||
|
import { items } from './config';
|
||||||
|
import { SideNavItem } from './side-nav-item';
|
||||||
|
|
||||||
|
export const SideNav = (props) => {
|
||||||
|
const { open, onClose } = props;
|
||||||
|
const pathname = usePathname();
|
||||||
|
const lgUp = useMediaQuery((theme) => theme.breakpoints.up('lg'));
|
||||||
|
|
||||||
|
const content = (
|
||||||
|
<Scrollbar
|
||||||
|
sx={{
|
||||||
|
height: '100%',
|
||||||
|
'& .simplebar-content': {
|
||||||
|
height: '100%'
|
||||||
|
},
|
||||||
|
'& .simplebar-scrollbar:before': {
|
||||||
|
background: 'neutral.400'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ p: 3 }}>
|
||||||
|
<Box
|
||||||
|
component={NextLink}
|
||||||
|
href="/"
|
||||||
|
sx={{
|
||||||
|
display: 'inline-flex',
|
||||||
|
height: 32,
|
||||||
|
width: 32
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Logo />
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.04)',
|
||||||
|
borderRadius: 1,
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
mt: 2,
|
||||||
|
p: '12px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Typography
|
||||||
|
color="inherit"
|
||||||
|
variant="subtitle1"
|
||||||
|
>
|
||||||
|
Devias
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
color="neutral.400"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
Production
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<SvgIcon
|
||||||
|
fontSize="small"
|
||||||
|
sx={{ color: 'neutral.500' }}
|
||||||
|
>
|
||||||
|
<ChevronUpDownIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Divider sx={{ borderColor: 'neutral.700' }} />
|
||||||
|
<Box
|
||||||
|
component="nav"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
px: 2,
|
||||||
|
py: 3
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
component="ul"
|
||||||
|
spacing={0.5}
|
||||||
|
sx={{
|
||||||
|
listStyle: 'none',
|
||||||
|
p: 0,
|
||||||
|
m: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{items.map((item) => {
|
||||||
|
const active = item.path ? (pathname === item.path) : false;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SideNavItem
|
||||||
|
active={active}
|
||||||
|
disabled={item.disabled}
|
||||||
|
external={item.external}
|
||||||
|
icon={item.icon}
|
||||||
|
key={item.title}
|
||||||
|
path={item.path}
|
||||||
|
title={item.title}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
<Divider sx={{ borderColor: 'neutral.700' }} />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
px: 2,
|
||||||
|
py: 3
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
color="neutral.100"
|
||||||
|
variant="subtitle2"
|
||||||
|
>
|
||||||
|
Need more features?
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
color="neutral.500"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
Check out our Pro solution template.
|
||||||
|
</Typography>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
mt: 2,
|
||||||
|
mx: 'auto',
|
||||||
|
width: '160px',
|
||||||
|
'& img': {
|
||||||
|
width: '100%'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="Go to pro"
|
||||||
|
src="/assets/devias-kit-pro.png"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Button
|
||||||
|
component="a"
|
||||||
|
endIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ArrowTopRightOnSquareIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
fullWidth
|
||||||
|
href="https://material-kit-pro-react.devias.io/"
|
||||||
|
sx={{ mt: 2 }}
|
||||||
|
target="_blank"
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Pro Live Preview
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Scrollbar>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lgUp) {
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
anchor="left"
|
||||||
|
open
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
backgroundColor: 'neutral.800',
|
||||||
|
color: 'common.white',
|
||||||
|
width: 280
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
variant="permanent"
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
anchor="left"
|
||||||
|
onClose={onClose}
|
||||||
|
open={open}
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
backgroundColor: 'neutral.800',
|
||||||
|
color: 'common.white',
|
||||||
|
width: 280
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
sx={{ zIndex: (theme) => theme.zIndex.appBar + 100 }}
|
||||||
|
variant="temporary"
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SideNav.propTypes = {
|
||||||
|
onClose: PropTypes.func,
|
||||||
|
open: PropTypes.bool
|
||||||
|
};
|
||||||
125
frontend/src/layouts/dashboard/top-nav.js
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import BellIcon from '@heroicons/react/24/solid/BellIcon';
|
||||||
|
import UsersIcon from '@heroicons/react/24/solid/UsersIcon';
|
||||||
|
import Bars3Icon from '@heroicons/react/24/solid/Bars3Icon';
|
||||||
|
import MagnifyingGlassIcon from '@heroicons/react/24/solid/MagnifyingGlassIcon';
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Badge,
|
||||||
|
Box,
|
||||||
|
IconButton,
|
||||||
|
Stack,
|
||||||
|
SvgIcon,
|
||||||
|
Tooltip,
|
||||||
|
useMediaQuery
|
||||||
|
} from '@mui/material';
|
||||||
|
import { alpha } from '@mui/material/styles';
|
||||||
|
import { usePopover } from 'src/hooks/use-popover';
|
||||||
|
import { AccountPopover } from './account-popover';
|
||||||
|
|
||||||
|
const SIDE_NAV_WIDTH = 280;
|
||||||
|
const TOP_NAV_HEIGHT = 64;
|
||||||
|
|
||||||
|
export const TopNav = (props) => {
|
||||||
|
const { onNavOpen } = props;
|
||||||
|
const lgUp = useMediaQuery((theme) => theme.breakpoints.up('lg'));
|
||||||
|
const accountPopover = usePopover();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
component="header"
|
||||||
|
sx={{
|
||||||
|
backdropFilter: 'blur(6px)',
|
||||||
|
backgroundColor: (theme) => alpha(theme.palette.background.default, 0.8),
|
||||||
|
position: 'sticky',
|
||||||
|
left: {
|
||||||
|
lg: `${SIDE_NAV_WIDTH}px`
|
||||||
|
},
|
||||||
|
top: 0,
|
||||||
|
width: {
|
||||||
|
lg: `calc(100% - ${SIDE_NAV_WIDTH}px)`
|
||||||
|
},
|
||||||
|
zIndex: (theme) => theme.zIndex.appBar
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
justifyContent="space-between"
|
||||||
|
spacing={2}
|
||||||
|
sx={{
|
||||||
|
minHeight: TOP_NAV_HEIGHT,
|
||||||
|
px: 2
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
spacing={2}
|
||||||
|
>
|
||||||
|
{!lgUp && (
|
||||||
|
<IconButton onClick={onNavOpen}>
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<Bars3Icon />
|
||||||
|
</SvgIcon>
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
<Tooltip title="Search">
|
||||||
|
<IconButton>
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<MagnifyingGlassIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
spacing={2}
|
||||||
|
>
|
||||||
|
<Tooltip title="Contacts">
|
||||||
|
<IconButton>
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<UsersIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Notifications">
|
||||||
|
<IconButton>
|
||||||
|
<Badge
|
||||||
|
badgeContent={4}
|
||||||
|
color="success"
|
||||||
|
variant="dot"
|
||||||
|
>
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<BellIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</Badge>
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
<Avatar
|
||||||
|
onClick={accountPopover.handleOpen}
|
||||||
|
ref={accountPopover.anchorRef}
|
||||||
|
sx={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
height: 40,
|
||||||
|
width: 40
|
||||||
|
}}
|
||||||
|
src="/assets/avatars/avatar-anika-visser.png"
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
<AccountPopover
|
||||||
|
anchorEl={accountPopover.anchorRef.current}
|
||||||
|
open={accountPopover.open}
|
||||||
|
onClose={accountPopover.handleClose}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
TopNav.propTypes = {
|
||||||
|
onNavOpen: PropTypes.func
|
||||||
|
};
|
||||||
80
frontend/src/pages/404.js
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import Head from 'next/head';
|
||||||
|
import NextLink from 'next/link';
|
||||||
|
import ArrowLeftIcon from '@heroicons/react/24/solid/ArrowLeftIcon';
|
||||||
|
import { Box, Button, Container, SvgIcon, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
const Page = () => (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
404 | Devias Kit
|
||||||
|
</title>
|
||||||
|
</Head>
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
flexGrow: 1,
|
||||||
|
minHeight: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="md">
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
mb: 3,
|
||||||
|
textAlign: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="Under development"
|
||||||
|
src="/assets/errors/error-404.png"
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
maxWidth: '100%',
|
||||||
|
width: 400
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
align="center"
|
||||||
|
sx={{ mb: 3 }}
|
||||||
|
variant="h3"
|
||||||
|
>
|
||||||
|
404: The page you are looking for isn’t here
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
align="center"
|
||||||
|
color="text.secondary"
|
||||||
|
variant="body1"
|
||||||
|
>
|
||||||
|
You either tried some shady route or you came here by mistake.
|
||||||
|
Whichever it is, try using the navigation
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
component={NextLink}
|
||||||
|
href="/"
|
||||||
|
startIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ArrowLeftIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
sx={{ mt: 3 }}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Go back to dashboard
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
55
frontend/src/pages/_app.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import Head from 'next/head';
|
||||||
|
import { CacheProvider } from '@emotion/react';
|
||||||
|
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
||||||
|
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||||
|
import { CssBaseline } from '@mui/material';
|
||||||
|
import { ThemeProvider } from '@mui/material/styles';
|
||||||
|
import { AuthConsumer, AuthProvider } from 'src/contexts/auth-context';
|
||||||
|
import { useNProgress } from 'src/hooks/use-nprogress';
|
||||||
|
import { createTheme } from 'src/theme';
|
||||||
|
import { createEmotionCache } from 'src/utils/create-emotion-cache';
|
||||||
|
import 'simplebar-react/dist/simplebar.min.css';
|
||||||
|
|
||||||
|
const clientSideEmotionCache = createEmotionCache();
|
||||||
|
|
||||||
|
const SplashScreen = () => null;
|
||||||
|
|
||||||
|
const App = (props) => {
|
||||||
|
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
|
||||||
|
|
||||||
|
useNProgress();
|
||||||
|
|
||||||
|
const getLayout = Component.getLayout ?? ((page) => page);
|
||||||
|
|
||||||
|
const theme = createTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CacheProvider value={emotionCache}>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
Devias Kit
|
||||||
|
</title>
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="initial-scale=1, width=device-width"
|
||||||
|
/>
|
||||||
|
</Head>
|
||||||
|
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||||
|
<AuthProvider>
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
<CssBaseline />
|
||||||
|
<AuthConsumer>
|
||||||
|
{
|
||||||
|
(auth) => auth.isLoading
|
||||||
|
? <SplashScreen />
|
||||||
|
: getLayout(<Component {...pageProps} />)
|
||||||
|
}
|
||||||
|
</AuthConsumer>
|
||||||
|
</ThemeProvider>
|
||||||
|
</AuthProvider>
|
||||||
|
</LocalizationProvider>
|
||||||
|
</CacheProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
105
frontend/src/pages/_document.js
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { Children } from 'react';
|
||||||
|
import Document, { Head, Html, Main, NextScript } from 'next/document';
|
||||||
|
import createEmotionServer from '@emotion/server/create-instance';
|
||||||
|
import { createEmotionCache } from 'src/utils/create-emotion-cache';
|
||||||
|
|
||||||
|
const Favicon = () => (
|
||||||
|
<>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
href="/favicon.ico"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Fonts = () => (
|
||||||
|
<>
|
||||||
|
<link
|
||||||
|
rel="preconnect"
|
||||||
|
href="https://fonts.googleapis.com"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="preconnect"
|
||||||
|
href="https://fonts.gstatic.com"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400&display=swap"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@600;700&display=swap"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
class CustomDocument extends Document {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Html lang="en">
|
||||||
|
<Head>
|
||||||
|
<Favicon />
|
||||||
|
<Fonts />
|
||||||
|
</Head>
|
||||||
|
<body>
|
||||||
|
<Main />
|
||||||
|
<NextScript />
|
||||||
|
</body>
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomDocument.getInitialProps = async (ctx) => {
|
||||||
|
const originalRenderPage = ctx.renderPage;
|
||||||
|
const cache = createEmotionCache();
|
||||||
|
const { extractCriticalToChunks } = createEmotionServer(cache);
|
||||||
|
|
||||||
|
ctx.renderPage = () => originalRenderPage({
|
||||||
|
enhanceApp: (App) => (props) => (
|
||||||
|
<App
|
||||||
|
emotionCache={cache}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialProps = await Document.getInitialProps(ctx);
|
||||||
|
const emotionStyles = extractCriticalToChunks(initialProps.html);
|
||||||
|
const emotionStyleTags = emotionStyles.styles.map((style) => (
|
||||||
|
<style
|
||||||
|
data-emotion={`${style.key} ${style.ids.join(' ')}`}
|
||||||
|
key={style.key}
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
|
dangerouslySetInnerHTML={{ __html: style.css }}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...initialProps,
|
||||||
|
styles: [...Children.toArray(initialProps.styles), ...emotionStyleTags]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomDocument;
|
||||||
61
frontend/src/pages/account.js
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import Head from 'next/head';
|
||||||
|
import { Box, Container, Stack, Typography, Unstable_Grid2 as Grid } from '@mui/material';
|
||||||
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
import { AccountProfile } from 'src/sections/account/account-profile';
|
||||||
|
import { AccountProfileDetails } from 'src/sections/account/account-profile-details';
|
||||||
|
|
||||||
|
const Page = () => (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
Account | Devias Kit
|
||||||
|
</title>
|
||||||
|
</Head>
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
py: 8
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="lg">
|
||||||
|
<Stack spacing={3}>
|
||||||
|
<div>
|
||||||
|
<Typography variant="h4">
|
||||||
|
Account
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={3}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
lg={4}
|
||||||
|
>
|
||||||
|
<AccountProfile />
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
lg={8}
|
||||||
|
>
|
||||||
|
<AccountProfileDetails />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<DashboardLayout>
|
||||||
|
{page}
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
227
frontend/src/pages/auth/login.js
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import Head from 'next/head';
|
||||||
|
import NextLink from 'next/link';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
FormHelperText,
|
||||||
|
Link,
|
||||||
|
Stack,
|
||||||
|
Tab,
|
||||||
|
Tabs,
|
||||||
|
TextField,
|
||||||
|
Typography
|
||||||
|
} from '@mui/material';
|
||||||
|
import { useAuth } from 'src/hooks/use-auth';
|
||||||
|
import { Layout as AuthLayout } from 'src/layouts/auth/layout';
|
||||||
|
|
||||||
|
const Page = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const auth = useAuth();
|
||||||
|
const [method, setMethod] = useState('email');
|
||||||
|
const formik = useFormik({
|
||||||
|
initialValues: {
|
||||||
|
email: 'demo@devias.io',
|
||||||
|
password: 'Password123!',
|
||||||
|
submit: null
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object({
|
||||||
|
email: Yup
|
||||||
|
.string()
|
||||||
|
.email('Must be a valid email')
|
||||||
|
.max(255)
|
||||||
|
.required('Email is required'),
|
||||||
|
password: Yup
|
||||||
|
.string()
|
||||||
|
.max(255)
|
||||||
|
.required('Password is required')
|
||||||
|
}),
|
||||||
|
onSubmit: async (values, helpers) => {
|
||||||
|
try {
|
||||||
|
await auth.signIn(values.email, values.password);
|
||||||
|
router.push('/');
|
||||||
|
} catch (err) {
|
||||||
|
helpers.setStatus({ success: false });
|
||||||
|
helpers.setErrors({ submit: err.message });
|
||||||
|
helpers.setSubmitting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleMethodChange = useCallback(
|
||||||
|
(event, value) => {
|
||||||
|
setMethod(value);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSkip = useCallback(
|
||||||
|
() => {
|
||||||
|
auth.skip();
|
||||||
|
router.push('/');
|
||||||
|
},
|
||||||
|
[auth, router]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
Login | Devias Kit
|
||||||
|
</title>
|
||||||
|
</Head>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
backgroundColor: 'background.paper',
|
||||||
|
flex: '1 1 auto',
|
||||||
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
maxWidth: 550,
|
||||||
|
px: 3,
|
||||||
|
py: '100px',
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Stack
|
||||||
|
spacing={1}
|
||||||
|
sx={{ mb: 3 }}
|
||||||
|
>
|
||||||
|
<Typography variant="h4">
|
||||||
|
Login
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
color="text.secondary"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
Don't have an account?
|
||||||
|
|
||||||
|
<Link
|
||||||
|
component={NextLink}
|
||||||
|
href="/auth/register"
|
||||||
|
underline="hover"
|
||||||
|
variant="subtitle2"
|
||||||
|
>
|
||||||
|
Register
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Tabs
|
||||||
|
onChange={handleMethodChange}
|
||||||
|
sx={{ mb: 3 }}
|
||||||
|
value={method}
|
||||||
|
>
|
||||||
|
<Tab
|
||||||
|
label="Email"
|
||||||
|
value="email"
|
||||||
|
/>
|
||||||
|
<Tab
|
||||||
|
label="Phone Number"
|
||||||
|
value="phoneNumber"
|
||||||
|
/>
|
||||||
|
</Tabs>
|
||||||
|
{method === 'email' && (
|
||||||
|
<form
|
||||||
|
noValidate
|
||||||
|
onSubmit={formik.handleSubmit}
|
||||||
|
>
|
||||||
|
<Stack spacing={3}>
|
||||||
|
<TextField
|
||||||
|
error={!!(formik.touched.email && formik.errors.email)}
|
||||||
|
fullWidth
|
||||||
|
helperText={formik.touched.email && formik.errors.email}
|
||||||
|
label="Email Address"
|
||||||
|
name="email"
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
type="email"
|
||||||
|
value={formik.values.email}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
error={!!(formik.touched.password && formik.errors.password)}
|
||||||
|
fullWidth
|
||||||
|
helperText={formik.touched.password && formik.errors.password}
|
||||||
|
label="Password"
|
||||||
|
name="password"
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
type="password"
|
||||||
|
value={formik.values.password}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<FormHelperText sx={{ mt: 1 }}>
|
||||||
|
Optionally you can skip.
|
||||||
|
</FormHelperText>
|
||||||
|
{formik.errors.submit && (
|
||||||
|
<Typography
|
||||||
|
color="error"
|
||||||
|
sx={{ mt: 3 }}
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
{formik.errors.submit}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
size="large"
|
||||||
|
sx={{ mt: 3 }}
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
size="large"
|
||||||
|
sx={{ mt: 3 }}
|
||||||
|
onClick={handleSkip}
|
||||||
|
>
|
||||||
|
Skip authentication
|
||||||
|
</Button>
|
||||||
|
<Alert
|
||||||
|
color="primary"
|
||||||
|
severity="info"
|
||||||
|
sx={{ mt: 3 }}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
You can use <b>demo@devias.io</b> and password <b>Password123!</b>
|
||||||
|
</div>
|
||||||
|
</Alert>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
{method === 'phoneNumber' && (
|
||||||
|
<div>
|
||||||
|
<Typography
|
||||||
|
sx={{ mb: 1 }}
|
||||||
|
variant="h6"
|
||||||
|
>
|
||||||
|
Not available in the demo
|
||||||
|
</Typography>
|
||||||
|
<Typography color="text.secondary">
|
||||||
|
To prevent unnecessary costs we disabled this feature in the demo.
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<AuthLayout>
|
||||||
|
{page}
|
||||||
|
</AuthLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
164
frontend/src/pages/auth/register.js
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
import Head from 'next/head';
|
||||||
|
import NextLink from 'next/link';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { Box, Button, Link, Stack, TextField, Typography } from '@mui/material';
|
||||||
|
import { useAuth } from 'src/hooks/use-auth';
|
||||||
|
import { Layout as AuthLayout } from 'src/layouts/auth/layout';
|
||||||
|
|
||||||
|
const Page = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const auth = useAuth();
|
||||||
|
const formik = useFormik({
|
||||||
|
initialValues: {
|
||||||
|
email: '',
|
||||||
|
name: '',
|
||||||
|
password: '',
|
||||||
|
submit: null
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object({
|
||||||
|
email: Yup
|
||||||
|
.string()
|
||||||
|
.email('Must be a valid email')
|
||||||
|
.max(255)
|
||||||
|
.required('Email is required'),
|
||||||
|
name: Yup
|
||||||
|
.string()
|
||||||
|
.max(255)
|
||||||
|
.required('Name is required'),
|
||||||
|
password: Yup
|
||||||
|
.string()
|
||||||
|
.max(255)
|
||||||
|
.required('Password is required')
|
||||||
|
}),
|
||||||
|
onSubmit: async (values, helpers) => {
|
||||||
|
try {
|
||||||
|
await auth.signUp(values.email, values.name, values.password);
|
||||||
|
router.push('/');
|
||||||
|
} catch (err) {
|
||||||
|
helpers.setStatus({ success: false });
|
||||||
|
helpers.setErrors({ submit: err.message });
|
||||||
|
helpers.setSubmitting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
Register | Devias Kit
|
||||||
|
</title>
|
||||||
|
</Head>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
flex: '1 1 auto',
|
||||||
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
maxWidth: 550,
|
||||||
|
px: 3,
|
||||||
|
py: '100px',
|
||||||
|
width: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<Stack
|
||||||
|
spacing={1}
|
||||||
|
sx={{ mb: 3 }}
|
||||||
|
>
|
||||||
|
<Typography variant="h4">
|
||||||
|
Register
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
color="text.secondary"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
Already have an account?
|
||||||
|
|
||||||
|
<Link
|
||||||
|
component={NextLink}
|
||||||
|
href="/auth/login"
|
||||||
|
underline="hover"
|
||||||
|
variant="subtitle2"
|
||||||
|
>
|
||||||
|
Log in
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<form
|
||||||
|
noValidate
|
||||||
|
onSubmit={formik.handleSubmit}
|
||||||
|
>
|
||||||
|
<Stack spacing={3}>
|
||||||
|
<TextField
|
||||||
|
error={!!(formik.touched.name && formik.errors.name)}
|
||||||
|
fullWidth
|
||||||
|
helperText={formik.touched.name && formik.errors.name}
|
||||||
|
label="Name"
|
||||||
|
name="name"
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.name}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
error={!!(formik.touched.email && formik.errors.email)}
|
||||||
|
fullWidth
|
||||||
|
helperText={formik.touched.email && formik.errors.email}
|
||||||
|
label="Email Address"
|
||||||
|
name="email"
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
type="email"
|
||||||
|
value={formik.values.email}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
error={!!(formik.touched.password && formik.errors.password)}
|
||||||
|
fullWidth
|
||||||
|
helperText={formik.touched.password && formik.errors.password}
|
||||||
|
label="Password"
|
||||||
|
name="password"
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
type="password"
|
||||||
|
value={formik.values.password}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
{formik.errors.submit && (
|
||||||
|
<Typography
|
||||||
|
color="error"
|
||||||
|
sx={{ mt: 3 }}
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
{formik.errors.submit}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
size="large"
|
||||||
|
sx={{ mt: 3 }}
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<AuthLayout>
|
||||||
|
{page}
|
||||||
|
</AuthLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
174
frontend/src/pages/companies.js
Normal file
|
|
@ -0,0 +1,174 @@
|
||||||
|
import Head from 'next/head';
|
||||||
|
import ArrowUpOnSquareIcon from '@heroicons/react/24/solid/ArrowUpOnSquareIcon';
|
||||||
|
import ArrowDownOnSquareIcon from '@heroicons/react/24/solid/ArrowDownOnSquareIcon';
|
||||||
|
import PlusIcon from '@heroicons/react/24/solid/PlusIcon';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Container,
|
||||||
|
Pagination,
|
||||||
|
Stack,
|
||||||
|
SvgIcon,
|
||||||
|
Typography,
|
||||||
|
Unstable_Grid2 as Grid
|
||||||
|
} from '@mui/material';
|
||||||
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
import { CompanyCard } from 'src/sections/companies/company-card';
|
||||||
|
import { CompaniesSearch } from 'src/sections/companies/companies-search';
|
||||||
|
|
||||||
|
const companies = [
|
||||||
|
{
|
||||||
|
id: '2569ce0d517a7f06d3ea1f24',
|
||||||
|
createdAt: '27/03/2019',
|
||||||
|
description: 'Dropbox is a file hosting service that offers cloud storage, file synchronization, a personal cloud.',
|
||||||
|
logo: '/assets/logos/logo-dropbox.png',
|
||||||
|
title: 'Dropbox',
|
||||||
|
downloads: '594'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ed2b900870ceba72d203ec15',
|
||||||
|
createdAt: '31/03/2019',
|
||||||
|
description: 'Medium is an online publishing platform developed by Evan Williams, and launched in August 2012.',
|
||||||
|
logo: '/assets/logos/logo-medium.png',
|
||||||
|
title: 'Medium Corporation',
|
||||||
|
downloads: '625'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'a033e38768c82fca90df3db7',
|
||||||
|
createdAt: '03/04/2019',
|
||||||
|
description: 'Slack is a cloud-based set of team collaboration tools and services, founded by Stewart Butterfield.',
|
||||||
|
logo: '/assets/logos/logo-slack.png',
|
||||||
|
title: 'Slack',
|
||||||
|
downloads: '857'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '1efecb2bf6a51def9869ab0f',
|
||||||
|
createdAt: '04/04/2019',
|
||||||
|
description: 'Lyft is an on-demand transportation company based in San Francisco, California.',
|
||||||
|
logo: '/assets/logos/logo-lyft.png',
|
||||||
|
title: 'Lyft',
|
||||||
|
downloads: '406'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '1ed68149f65fbc6089b5fd07',
|
||||||
|
createdAt: '04/04/2019',
|
||||||
|
description: 'GitHub is a web-based hosting service for version control of code using Git.',
|
||||||
|
logo: '/assets/logos/logo-github.png',
|
||||||
|
title: 'GitHub',
|
||||||
|
downloads: '835'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5dab321376eff6177407e887',
|
||||||
|
createdAt: '04/04/2019',
|
||||||
|
description: 'Squarespace provides software as a service for website building and hosting. Headquartered in NYC.',
|
||||||
|
logo: '/assets/logos/logo-squarespace.png',
|
||||||
|
title: 'Squarespace',
|
||||||
|
downloads: '835'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const Page = () => (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
Companies | Devias Kit
|
||||||
|
</title>
|
||||||
|
</Head>
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
py: 8
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="xl">
|
||||||
|
<Stack spacing={3}>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
justifyContent="space-between"
|
||||||
|
spacing={4}
|
||||||
|
>
|
||||||
|
<Stack spacing={1}>
|
||||||
|
<Typography variant="h4">
|
||||||
|
Companies
|
||||||
|
</Typography>
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
spacing={1}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="inherit"
|
||||||
|
startIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ArrowUpOnSquareIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="inherit"
|
||||||
|
startIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ArrowDownOnSquareIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
startIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<PlusIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
<CompaniesSearch />
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={3}
|
||||||
|
>
|
||||||
|
{companies.map((company) => (
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
lg={4}
|
||||||
|
key={company.id}
|
||||||
|
>
|
||||||
|
<CompanyCard company={company} />
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Pagination
|
||||||
|
count={3}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<DashboardLayout>
|
||||||
|
{page}
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
290
frontend/src/pages/customers.js
Normal file
|
|
@ -0,0 +1,290 @@
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import Head from 'next/head';
|
||||||
|
import { subDays, subHours } from 'date-fns';
|
||||||
|
import ArrowDownOnSquareIcon from '@heroicons/react/24/solid/ArrowDownOnSquareIcon';
|
||||||
|
import ArrowUpOnSquareIcon from '@heroicons/react/24/solid/ArrowUpOnSquareIcon';
|
||||||
|
import PlusIcon from '@heroicons/react/24/solid/PlusIcon';
|
||||||
|
import { Box, Button, Container, Stack, SvgIcon, Typography } from '@mui/material';
|
||||||
|
import { useSelection } from 'src/hooks/use-selection';
|
||||||
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
import { CustomersTable } from 'src/sections/customer/customers-table';
|
||||||
|
import { CustomersSearch } from 'src/sections/customer/customers-search';
|
||||||
|
import { applyPagination } from 'src/utils/apply-pagination';
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
id: '5e887ac47eed253091be10cb',
|
||||||
|
address: {
|
||||||
|
city: 'Cleveland',
|
||||||
|
country: 'USA',
|
||||||
|
state: 'Ohio',
|
||||||
|
street: '2849 Fulton Street'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-carson-darrin.png',
|
||||||
|
createdAt: subDays(subHours(now, 7), 1).getTime(),
|
||||||
|
email: 'carson.darrin@devias.io',
|
||||||
|
name: 'Carson Darrin',
|
||||||
|
phone: '304-428-3097'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5e887b209c28ac3dd97f6db5',
|
||||||
|
address: {
|
||||||
|
city: 'Atlanta',
|
||||||
|
country: 'USA',
|
||||||
|
state: 'Georgia',
|
||||||
|
street: '1865 Pleasant Hill Road'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-fran-perez.png',
|
||||||
|
createdAt: subDays(subHours(now, 1), 2).getTime(),
|
||||||
|
email: 'fran.perez@devias.io',
|
||||||
|
name: 'Fran Perez',
|
||||||
|
phone: '712-351-5711'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5e887b7602bdbc4dbb234b27',
|
||||||
|
address: {
|
||||||
|
city: 'North Canton',
|
||||||
|
country: 'USA',
|
||||||
|
state: 'Ohio',
|
||||||
|
street: '4894 Lakeland Park Drive'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-jie-yan-song.png',
|
||||||
|
createdAt: subDays(subHours(now, 4), 2).getTime(),
|
||||||
|
email: 'jie.yan.song@devias.io',
|
||||||
|
name: 'Jie Yan Song',
|
||||||
|
phone: '770-635-2682'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5e86809283e28b96d2d38537',
|
||||||
|
address: {
|
||||||
|
city: 'Madrid',
|
||||||
|
country: 'Spain',
|
||||||
|
name: 'Anika Visser',
|
||||||
|
street: '4158 Hedge Street'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-anika-visser.png',
|
||||||
|
createdAt: subDays(subHours(now, 11), 2).getTime(),
|
||||||
|
email: 'anika.visser@devias.io',
|
||||||
|
name: 'Anika Visser',
|
||||||
|
phone: '908-691-3242'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5e86805e2bafd54f66cc95c3',
|
||||||
|
address: {
|
||||||
|
city: 'San Diego',
|
||||||
|
country: 'USA',
|
||||||
|
state: 'California',
|
||||||
|
street: '75247'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-miron-vitold.png',
|
||||||
|
createdAt: subDays(subHours(now, 7), 3).getTime(),
|
||||||
|
email: 'miron.vitold@devias.io',
|
||||||
|
name: 'Miron Vitold',
|
||||||
|
phone: '972-333-4106'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5e887a1fbefd7938eea9c981',
|
||||||
|
address: {
|
||||||
|
city: 'Berkeley',
|
||||||
|
country: 'USA',
|
||||||
|
state: 'California',
|
||||||
|
street: '317 Angus Road'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-penjani-inyene.png',
|
||||||
|
createdAt: subDays(subHours(now, 5), 4).getTime(),
|
||||||
|
email: 'penjani.inyene@devias.io',
|
||||||
|
name: 'Penjani Inyene',
|
||||||
|
phone: '858-602-3409'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5e887d0b3d090c1b8f162003',
|
||||||
|
address: {
|
||||||
|
city: 'Carson City',
|
||||||
|
country: 'USA',
|
||||||
|
state: 'Nevada',
|
||||||
|
street: '2188 Armbrester Drive'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-omar-darboe.png',
|
||||||
|
createdAt: subDays(subHours(now, 15), 4).getTime(),
|
||||||
|
email: 'omar.darobe@devias.io',
|
||||||
|
name: 'Omar Darobe',
|
||||||
|
phone: '415-907-2647'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5e88792be2d4cfb4bf0971d9',
|
||||||
|
address: {
|
||||||
|
city: 'Los Angeles',
|
||||||
|
country: 'USA',
|
||||||
|
state: 'California',
|
||||||
|
street: '1798 Hickory Ridge Drive'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-siegbert-gottfried.png',
|
||||||
|
createdAt: subDays(subHours(now, 2), 5).getTime(),
|
||||||
|
email: 'siegbert.gottfried@devias.io',
|
||||||
|
name: 'Siegbert Gottfried',
|
||||||
|
phone: '702-661-1654'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5e8877da9a65442b11551975',
|
||||||
|
address: {
|
||||||
|
city: 'Murray',
|
||||||
|
country: 'USA',
|
||||||
|
state: 'Utah',
|
||||||
|
street: '3934 Wildrose Lane'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-iulia-albu.png',
|
||||||
|
createdAt: subDays(subHours(now, 8), 6).getTime(),
|
||||||
|
email: 'iulia.albu@devias.io',
|
||||||
|
name: 'Iulia Albu',
|
||||||
|
phone: '313-812-8947'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5e8680e60cba5019c5ca6fda',
|
||||||
|
address: {
|
||||||
|
city: 'Salt Lake City',
|
||||||
|
country: 'USA',
|
||||||
|
state: 'Utah',
|
||||||
|
street: '368 Lamberts Branch Road'
|
||||||
|
},
|
||||||
|
avatar: '/assets/avatars/avatar-nasimiyu-danai.png',
|
||||||
|
createdAt: subDays(subHours(now, 1), 9).getTime(),
|
||||||
|
email: 'nasimiyu.danai@devias.io',
|
||||||
|
name: 'Nasimiyu Danai',
|
||||||
|
phone: '801-301-7894'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const useCustomers = (page, rowsPerPage) => {
|
||||||
|
return useMemo(
|
||||||
|
() => {
|
||||||
|
return applyPagination(data, page, rowsPerPage);
|
||||||
|
},
|
||||||
|
[page, rowsPerPage]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const useCustomerIds = (customers) => {
|
||||||
|
return useMemo(
|
||||||
|
() => {
|
||||||
|
return customers.map((customer) => customer.id);
|
||||||
|
},
|
||||||
|
[customers]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Page = () => {
|
||||||
|
const [page, setPage] = useState(0);
|
||||||
|
const [rowsPerPage, setRowsPerPage] = useState(5);
|
||||||
|
const customers = useCustomers(page, rowsPerPage);
|
||||||
|
const customersIds = useCustomerIds(customers);
|
||||||
|
const customersSelection = useSelection(customersIds);
|
||||||
|
|
||||||
|
const handlePageChange = useCallback(
|
||||||
|
(event, value) => {
|
||||||
|
setPage(value);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleRowsPerPageChange = useCallback(
|
||||||
|
(event) => {
|
||||||
|
setRowsPerPage(event.target.value);
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
Customers | Devias Kit
|
||||||
|
</title>
|
||||||
|
</Head>
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
py: 8
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="xl">
|
||||||
|
<Stack spacing={3}>
|
||||||
|
<Stack
|
||||||
|
direction="row"
|
||||||
|
justifyContent="space-between"
|
||||||
|
spacing={4}
|
||||||
|
>
|
||||||
|
<Stack spacing={1}>
|
||||||
|
<Typography variant="h4">
|
||||||
|
Customers
|
||||||
|
</Typography>
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
spacing={1}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="inherit"
|
||||||
|
startIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ArrowUpOnSquareIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="inherit"
|
||||||
|
startIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ArrowDownOnSquareIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
startIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<PlusIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
<CustomersSearch />
|
||||||
|
<CustomersTable
|
||||||
|
count={data.length}
|
||||||
|
items={customers}
|
||||||
|
onDeselectAll={customersSelection.handleDeselectAll}
|
||||||
|
onDeselectOne={customersSelection.handleDeselectOne}
|
||||||
|
onPageChange={handlePageChange}
|
||||||
|
onRowsPerPageChange={handleRowsPerPageChange}
|
||||||
|
onSelectAll={customersSelection.handleSelectAll}
|
||||||
|
onSelectOne={customersSelection.handleSelectOne}
|
||||||
|
page={page}
|
||||||
|
rowsPerPage={rowsPerPage}
|
||||||
|
selected={customersSelection.selected}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<DashboardLayout>
|
||||||
|
{page}
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
232
frontend/src/pages/index.js
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
import Head from 'next/head';
|
||||||
|
import { subDays, subHours } from 'date-fns';
|
||||||
|
import { Box, Container, Unstable_Grid2 as Grid } from '@mui/material';
|
||||||
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
import { OverviewBudget } from 'src/sections/overview/overview-budget';
|
||||||
|
import { OverviewLatestOrders } from 'src/sections/overview/overview-latest-orders';
|
||||||
|
import { OverviewLatestProducts } from 'src/sections/overview/overview-latest-products';
|
||||||
|
import { OverviewSales } from 'src/sections/overview/overview-sales';
|
||||||
|
import { OverviewTasksProgress } from 'src/sections/overview/overview-tasks-progress';
|
||||||
|
import { OverviewTotalCustomers } from 'src/sections/overview/overview-total-customers';
|
||||||
|
import { OverviewTotalProfit } from 'src/sections/overview/overview-total-profit';
|
||||||
|
import { OverviewTraffic } from 'src/sections/overview/overview-traffic';
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
const Page = () => (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
Overview | Devias Kit
|
||||||
|
</title>
|
||||||
|
</Head>
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
py: 8
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="xl">
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={3}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
sm={6}
|
||||||
|
lg={3}
|
||||||
|
>
|
||||||
|
<OverviewBudget
|
||||||
|
difference={12}
|
||||||
|
positive
|
||||||
|
sx={{ height: '100%' }}
|
||||||
|
value="$24k"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
sm={6}
|
||||||
|
lg={3}
|
||||||
|
>
|
||||||
|
<OverviewTotalCustomers
|
||||||
|
difference={16}
|
||||||
|
positive={false}
|
||||||
|
sx={{ height: '100%' }}
|
||||||
|
value="1.6k"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
sm={6}
|
||||||
|
lg={3}
|
||||||
|
>
|
||||||
|
<OverviewTasksProgress
|
||||||
|
sx={{ height: '100%' }}
|
||||||
|
value={75.5}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
sm={6}
|
||||||
|
lg={3}
|
||||||
|
>
|
||||||
|
<OverviewTotalProfit
|
||||||
|
sx={{ height: '100%' }}
|
||||||
|
value="$15k"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
lg={8}
|
||||||
|
>
|
||||||
|
<OverviewSales
|
||||||
|
chartSeries={[
|
||||||
|
{
|
||||||
|
name: 'This year',
|
||||||
|
data: [18, 16, 5, 8, 3, 14, 14, 16, 17, 19, 18, 20]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Last year',
|
||||||
|
data: [12, 11, 4, 6, 2, 9, 9, 10, 11, 12, 13, 13]
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
sx={{ height: '100%' }}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
lg={4}
|
||||||
|
>
|
||||||
|
<OverviewTraffic
|
||||||
|
chartSeries={[63, 15, 22]}
|
||||||
|
labels={['Desktop', 'Tablet', 'Phone']}
|
||||||
|
sx={{ height: '100%' }}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
lg={4}
|
||||||
|
>
|
||||||
|
<OverviewLatestProducts
|
||||||
|
products={[
|
||||||
|
{
|
||||||
|
id: '5ece2c077e39da27658aa8a9',
|
||||||
|
image: '/assets/products/product-1.png',
|
||||||
|
name: 'Healthcare Erbology',
|
||||||
|
updatedAt: subHours(now, 6).getTime()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5ece2c0d16f70bff2cf86cd8',
|
||||||
|
image: '/assets/products/product-2.png',
|
||||||
|
name: 'Makeup Lancome Rouge',
|
||||||
|
updatedAt: subDays(subHours(now, 8), 2).getTime()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'b393ce1b09c1254c3a92c827',
|
||||||
|
image: '/assets/products/product-5.png',
|
||||||
|
name: 'Skincare Soja CO',
|
||||||
|
updatedAt: subDays(subHours(now, 1), 1).getTime()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'a6ede15670da63f49f752c89',
|
||||||
|
image: '/assets/products/product-6.png',
|
||||||
|
name: 'Makeup Lipstick',
|
||||||
|
updatedAt: subDays(subHours(now, 3), 3).getTime()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'bcad5524fe3a2f8f8620ceda',
|
||||||
|
image: '/assets/products/product-7.png',
|
||||||
|
name: 'Healthcare Ritual',
|
||||||
|
updatedAt: subDays(subHours(now, 5), 6).getTime()
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
sx={{ height: '100%' }}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={12}
|
||||||
|
lg={8}
|
||||||
|
>
|
||||||
|
<OverviewLatestOrders
|
||||||
|
orders={[
|
||||||
|
{
|
||||||
|
id: 'f69f88012978187a6c12897f',
|
||||||
|
ref: 'DEV1049',
|
||||||
|
amount: 30.5,
|
||||||
|
customer: {
|
||||||
|
name: 'Ekaterina Tankova'
|
||||||
|
},
|
||||||
|
createdAt: 1555016400000,
|
||||||
|
status: 'pending'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9eaa1c7dd4433f413c308ce2',
|
||||||
|
ref: 'DEV1048',
|
||||||
|
amount: 25.1,
|
||||||
|
customer: {
|
||||||
|
name: 'Cao Yu'
|
||||||
|
},
|
||||||
|
createdAt: 1555016400000,
|
||||||
|
status: 'delivered'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '01a5230c811bd04996ce7c13',
|
||||||
|
ref: 'DEV1047',
|
||||||
|
amount: 10.99,
|
||||||
|
customer: {
|
||||||
|
name: 'Alexa Richardson'
|
||||||
|
},
|
||||||
|
createdAt: 1554930000000,
|
||||||
|
status: 'refunded'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '1f4e1bd0a87cea23cdb83d18',
|
||||||
|
ref: 'DEV1046',
|
||||||
|
amount: 96.43,
|
||||||
|
customer: {
|
||||||
|
name: 'Anje Keizer'
|
||||||
|
},
|
||||||
|
createdAt: 1554757200000,
|
||||||
|
status: 'pending'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9f974f239d29ede969367103',
|
||||||
|
ref: 'DEV1045',
|
||||||
|
amount: 32.54,
|
||||||
|
customer: {
|
||||||
|
name: 'Clarke Gillebert'
|
||||||
|
},
|
||||||
|
createdAt: 1554670800000,
|
||||||
|
status: 'delivered'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ffc83c1560ec2f66a1c05596',
|
||||||
|
ref: 'DEV1044',
|
||||||
|
amount: 16.76,
|
||||||
|
customer: {
|
||||||
|
name: 'Adam Denisov'
|
||||||
|
},
|
||||||
|
createdAt: 1554670800000,
|
||||||
|
status: 'delivered'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
sx={{ height: '100%' }}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<DashboardLayout>
|
||||||
|
{page}
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
40
frontend/src/pages/settings.js
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import Head from 'next/head';
|
||||||
|
import { Box, Container, Stack, Typography } from '@mui/material';
|
||||||
|
import { SettingsNotifications } from 'src/sections/settings/settings-notifications';
|
||||||
|
import { SettingsPassword } from 'src/sections/settings/settings-password';
|
||||||
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
|
||||||
|
const Page = () => (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>
|
||||||
|
Settings | Devias Kit
|
||||||
|
</title>
|
||||||
|
</Head>
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
py: 8
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="lg">
|
||||||
|
<Stack spacing={3}>
|
||||||
|
<Typography variant="h4">
|
||||||
|
Settings
|
||||||
|
</Typography>
|
||||||
|
<SettingsNotifications />
|
||||||
|
<SettingsPassword />
|
||||||
|
</Stack>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<DashboardLayout>
|
||||||
|
{page}
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
179
frontend/src/sections/account/account-profile-details.js
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardActions,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
Divider,
|
||||||
|
TextField,
|
||||||
|
Unstable_Grid2 as Grid
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
const states = [
|
||||||
|
{
|
||||||
|
value: 'alabama',
|
||||||
|
label: 'Alabama'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'new-york',
|
||||||
|
label: 'New York'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'san-francisco',
|
||||||
|
label: 'San Francisco'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'los-angeles',
|
||||||
|
label: 'Los Angeles'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const AccountProfileDetails = () => {
|
||||||
|
const [values, setValues] = useState({
|
||||||
|
firstName: 'Anika',
|
||||||
|
lastName: 'Visser',
|
||||||
|
email: 'demo@devias.io',
|
||||||
|
phone: '',
|
||||||
|
state: 'los-angeles',
|
||||||
|
country: 'USA'
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(event) => {
|
||||||
|
setValues((prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
[event.target.name]: event.target.value
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSubmit = useCallback(
|
||||||
|
(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
autoComplete="off"
|
||||||
|
noValidate
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<Card>
|
||||||
|
<CardHeader
|
||||||
|
subheader="The information can be edited"
|
||||||
|
title="Profile"
|
||||||
|
/>
|
||||||
|
<CardContent sx={{ pt: 0 }}>
|
||||||
|
<Box sx={{ m: -1.5 }}>
|
||||||
|
<Grid
|
||||||
|
container
|
||||||
|
spacing={3}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
helperText="Please specify the first name"
|
||||||
|
label="First name"
|
||||||
|
name="firstName"
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
value={values.firstName}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Last name"
|
||||||
|
name="lastName"
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
value={values.lastName}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Email Address"
|
||||||
|
name="email"
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
value={values.email}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Phone Number"
|
||||||
|
name="phone"
|
||||||
|
onChange={handleChange}
|
||||||
|
type="number"
|
||||||
|
value={values.phone}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Country"
|
||||||
|
name="country"
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
value={values.country}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid
|
||||||
|
xs={12}
|
||||||
|
md={6}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="Select State"
|
||||||
|
name="state"
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
select
|
||||||
|
SelectProps={{ native: true }}
|
||||||
|
value={values.state}
|
||||||
|
>
|
||||||
|
{states.map((option) => (
|
||||||
|
<option
|
||||||
|
key={option.value}
|
||||||
|
value={option.value}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</CardContent>
|
||||||
|
<Divider />
|
||||||
|
<CardActions sx={{ justifyContent: 'flex-end' }}>
|
||||||
|
<Button variant="contained">
|
||||||
|
Save details
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
69
frontend/src/sections/account/account-profile.js
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardActions,
|
||||||
|
CardContent,
|
||||||
|
Divider,
|
||||||
|
Typography
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
const user = {
|
||||||
|
avatar: '/assets/avatars/avatar-anika-visser.png',
|
||||||
|
city: 'Los Angeles',
|
||||||
|
country: 'USA',
|
||||||
|
jobTitle: 'Senior Developer',
|
||||||
|
name: 'Anika Visser',
|
||||||
|
timezone: 'GTM-7'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AccountProfile = () => (
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
src={user.avatar}
|
||||||
|
sx={{
|
||||||
|
height: 80,
|
||||||
|
mb: 2,
|
||||||
|
width: 80
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
gutterBottom
|
||||||
|
variant="h5"
|
||||||
|
>
|
||||||
|
{user.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
color="text.secondary"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
{user.city} {user.country}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
color="text.secondary"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
{user.timezone}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</CardContent>
|
||||||
|
<Divider />
|
||||||
|
<CardActions>
|
||||||
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="text"
|
||||||
|
>
|
||||||
|
Upload picture
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
23
frontend/src/sections/companies/companies-search.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import MagnifyingGlassIcon from '@heroicons/react/24/solid/MagnifyingGlassIcon';
|
||||||
|
import { Card, InputAdornment, OutlinedInput, SvgIcon } from '@mui/material';
|
||||||
|
|
||||||
|
export const CompaniesSearch = () => (
|
||||||
|
<Card sx={{ p: 2 }}>
|
||||||
|
<OutlinedInput
|
||||||
|
defaultValue=""
|
||||||
|
fullWidth
|
||||||
|
placeholder="Search company"
|
||||||
|
startAdornment={(
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SvgIcon
|
||||||
|
color="action"
|
||||||
|
fontSize="small"
|
||||||
|
>
|
||||||
|
<MagnifyingGlassIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</InputAdornment>
|
||||||
|
)}
|
||||||
|
sx={{ maxWidth: 500 }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
98
frontend/src/sections/companies/company-card.js
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ArrowDownOnSquareIcon from '@heroicons/react/24/solid/ArrowDownOnSquareIcon';
|
||||||
|
import ClockIcon from '@heroicons/react/24/solid/ClockIcon';
|
||||||
|
import { Avatar, Box, Card, CardContent, Divider, Stack, SvgIcon, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
export const CompanyCard = (props) => {
|
||||||
|
const { company } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
pb: 3
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
src={company.logo}
|
||||||
|
variant="square"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Typography
|
||||||
|
align="center"
|
||||||
|
gutterBottom
|
||||||
|
variant="h5"
|
||||||
|
>
|
||||||
|
{company.title}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
align="center"
|
||||||
|
variant="body1"
|
||||||
|
>
|
||||||
|
{company.description}
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
<Box sx={{ flexGrow: 1 }} />
|
||||||
|
<Divider />
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
justifyContent="space-between"
|
||||||
|
spacing={2}
|
||||||
|
sx={{ p: 2 }}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
spacing={1}
|
||||||
|
>
|
||||||
|
<SvgIcon
|
||||||
|
color="action"
|
||||||
|
fontSize="small"
|
||||||
|
>
|
||||||
|
<ClockIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
<Typography
|
||||||
|
color="text.secondary"
|
||||||
|
display="inline"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
Updated 2hr ago
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
spacing={1}
|
||||||
|
>
|
||||||
|
<SvgIcon
|
||||||
|
color="action"
|
||||||
|
fontSize="small"
|
||||||
|
>
|
||||||
|
<ArrowDownOnSquareIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
<Typography
|
||||||
|
color="text.secondary"
|
||||||
|
display="inline"
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
{company.downloads} Downloads
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CompanyCard.propTypes = {
|
||||||
|
company: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
23
frontend/src/sections/customer/customers-search.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import MagnifyingGlassIcon from '@heroicons/react/24/solid/MagnifyingGlassIcon';
|
||||||
|
import { Card, InputAdornment, OutlinedInput, SvgIcon } from '@mui/material';
|
||||||
|
|
||||||
|
export const CustomersSearch = () => (
|
||||||
|
<Card sx={{ p: 2 }}>
|
||||||
|
<OutlinedInput
|
||||||
|
defaultValue=""
|
||||||
|
fullWidth
|
||||||
|
placeholder="Search customer"
|
||||||
|
startAdornment={(
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SvgIcon
|
||||||
|
color="action"
|
||||||
|
fontSize="small"
|
||||||
|
>
|
||||||
|
<MagnifyingGlassIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</InputAdornment>
|
||||||
|
)}
|
||||||
|
sx={{ maxWidth: 500 }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
156
frontend/src/sections/customer/customers-table.js
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
Card,
|
||||||
|
Checkbox,
|
||||||
|
Stack,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TablePagination,
|
||||||
|
TableRow,
|
||||||
|
Typography
|
||||||
|
} from '@mui/material';
|
||||||
|
import { Scrollbar } from 'src/components/scrollbar';
|
||||||
|
import { getInitials } from 'src/utils/get-initials';
|
||||||
|
|
||||||
|
export const CustomersTable = (props) => {
|
||||||
|
const {
|
||||||
|
count = 0,
|
||||||
|
items = [],
|
||||||
|
onDeselectAll,
|
||||||
|
onDeselectOne,
|
||||||
|
onPageChange = () => {},
|
||||||
|
onRowsPerPageChange,
|
||||||
|
onSelectAll,
|
||||||
|
onSelectOne,
|
||||||
|
page = 0,
|
||||||
|
rowsPerPage = 0,
|
||||||
|
selected = []
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const selectedSome = (selected.length > 0) && (selected.length < items.length);
|
||||||
|
const selectedAll = (items.length > 0) && (selected.length === items.length);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<Scrollbar>
|
||||||
|
<Box sx={{ minWidth: 800 }}>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell padding="checkbox">
|
||||||
|
<Checkbox
|
||||||
|
checked={selectedAll}
|
||||||
|
indeterminate={selectedSome}
|
||||||
|
onChange={(event) => {
|
||||||
|
if (event.target.checked) {
|
||||||
|
onSelectAll?.();
|
||||||
|
} else {
|
||||||
|
onDeselectAll?.();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
Name
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
Email
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
Location
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
Phone
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
Signed Up
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{items.map((customer) => {
|
||||||
|
const isSelected = selected.includes(customer.id);
|
||||||
|
const createdAt = format(customer.createdAt, 'dd/MM/yyyy');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
hover
|
||||||
|
key={customer.id}
|
||||||
|
selected={isSelected}
|
||||||
|
>
|
||||||
|
<TableCell padding="checkbox">
|
||||||
|
<Checkbox
|
||||||
|
checked={isSelected}
|
||||||
|
onChange={(event) => {
|
||||||
|
if (event.target.checked) {
|
||||||
|
onSelectOne?.(customer.id);
|
||||||
|
} else {
|
||||||
|
onDeselectOne?.(customer.id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
spacing={2}
|
||||||
|
>
|
||||||
|
<Avatar src={customer.avatar}>
|
||||||
|
{getInitials(customer.name)}
|
||||||
|
</Avatar>
|
||||||
|
<Typography variant="subtitle2">
|
||||||
|
{customer.name}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{customer.email}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{customer.address.city}, {customer.address.state}, {customer.address.country}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{customer.phone}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{createdAt}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
|
</Scrollbar>
|
||||||
|
<TablePagination
|
||||||
|
component="div"
|
||||||
|
count={count}
|
||||||
|
onPageChange={onPageChange}
|
||||||
|
onRowsPerPageChange={onRowsPerPageChange}
|
||||||
|
page={page}
|
||||||
|
rowsPerPage={rowsPerPage}
|
||||||
|
rowsPerPageOptions={[5, 10, 25]}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CustomersTable.propTypes = {
|
||||||
|
count: PropTypes.number,
|
||||||
|
items: PropTypes.array,
|
||||||
|
onDeselectAll: PropTypes.func,
|
||||||
|
onDeselectOne: PropTypes.func,
|
||||||
|
onPageChange: PropTypes.func,
|
||||||
|
onRowsPerPageChange: PropTypes.func,
|
||||||
|
onSelectAll: PropTypes.func,
|
||||||
|
onSelectOne: PropTypes.func,
|
||||||
|
page: PropTypes.number,
|
||||||
|
rowsPerPage: PropTypes.number,
|
||||||
|
selected: PropTypes.array
|
||||||
|
};
|
||||||
85
frontend/src/sections/overview/overview-budget.js
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ArrowDownIcon from '@heroicons/react/24/solid/ArrowDownIcon';
|
||||||
|
import ArrowUpIcon from '@heroicons/react/24/solid/ArrowUpIcon';
|
||||||
|
import CurrencyDollarIcon from '@heroicons/react/24/solid/CurrencyDollarIcon';
|
||||||
|
import { Avatar, Card, CardContent, Stack, SvgIcon, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
export const OverviewBudget = (props) => {
|
||||||
|
const { difference, positive = false, sx, value } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card sx={sx}>
|
||||||
|
<CardContent>
|
||||||
|
<Stack
|
||||||
|
alignItems="flex-start"
|
||||||
|
direction="row"
|
||||||
|
justifyContent="space-between"
|
||||||
|
spacing={3}
|
||||||
|
>
|
||||||
|
<Stack spacing={1}>
|
||||||
|
<Typography
|
||||||
|
color="text.secondary"
|
||||||
|
variant="overline"
|
||||||
|
>
|
||||||
|
Budget
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h4">
|
||||||
|
{value}
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Avatar
|
||||||
|
sx={{
|
||||||
|
backgroundColor: 'error.main',
|
||||||
|
height: 56,
|
||||||
|
width: 56
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SvgIcon>
|
||||||
|
<CurrencyDollarIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</Avatar>
|
||||||
|
</Stack>
|
||||||
|
{difference && (
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
spacing={2}
|
||||||
|
sx={{ mt: 2 }}
|
||||||
|
>
|
||||||
|
<Stack
|
||||||
|
alignItems="center"
|
||||||
|
direction="row"
|
||||||
|
spacing={0.5}
|
||||||
|
>
|
||||||
|
<SvgIcon
|
||||||
|
color={positive ? 'success' : 'error'}
|
||||||
|
fontSize="small"
|
||||||
|
>
|
||||||
|
{positive ? <ArrowUpIcon /> : <ArrowDownIcon />}
|
||||||
|
</SvgIcon>
|
||||||
|
<Typography
|
||||||
|
color={positive ? 'success.main' : 'error.main'}
|
||||||
|
variant="body2"
|
||||||
|
>
|
||||||
|
{difference}%
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
<Typography
|
||||||
|
color="text.secondary"
|
||||||
|
variant="caption"
|
||||||
|
>
|
||||||
|
Since last month
|
||||||
|
</Typography>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
OverviewBudget.prototypes = {
|
||||||
|
difference: PropTypes.number,
|
||||||
|
positive: PropTypes.bool,
|
||||||
|
sx: PropTypes.object,
|
||||||
|
value: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
104
frontend/src/sections/overview/overview-latest-orders.js
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ArrowRightIcon from '@heroicons/react/24/solid/ArrowRightIcon';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardActions,
|
||||||
|
CardHeader,
|
||||||
|
Divider,
|
||||||
|
SvgIcon,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableRow
|
||||||
|
} from '@mui/material';
|
||||||
|
import { Scrollbar } from 'src/components/scrollbar';
|
||||||
|
import { SeverityPill } from 'src/components/severity-pill';
|
||||||
|
|
||||||
|
const statusMap = {
|
||||||
|
pending: 'warning',
|
||||||
|
delivered: 'success',
|
||||||
|
refunded: 'error'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OverviewLatestOrders = (props) => {
|
||||||
|
const { orders = [], sx } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card sx={sx}>
|
||||||
|
<CardHeader title="Latest Orders" />
|
||||||
|
<Scrollbar sx={{ flexGrow: 1 }}>
|
||||||
|
<Box sx={{ minWidth: 800 }}>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>
|
||||||
|
Order
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
Customer
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sortDirection="desc">
|
||||||
|
Date
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
Status
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{orders.map((order) => {
|
||||||
|
const createdAt = format(order.createdAt, 'dd/MM/yyyy');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
hover
|
||||||
|
key={order.id}
|
||||||
|
>
|
||||||
|
<TableCell>
|
||||||
|
{order.ref}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{order.customer.name}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{createdAt}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<SeverityPill color={statusMap[order.status]}>
|
||||||
|
{order.status}
|
||||||
|
</SeverityPill>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
|
</Scrollbar>
|
||||||
|
<Divider />
|
||||||
|
<CardActions sx={{ justifyContent: 'flex-end' }}>
|
||||||
|
<Button
|
||||||
|
color="inherit"
|
||||||
|
endIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ArrowRightIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
size="small"
|
||||||
|
variant="text"
|
||||||
|
>
|
||||||
|
View all
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
OverviewLatestOrders.prototype = {
|
||||||
|
orders: PropTypes.array,
|
||||||
|
sx: PropTypes.object
|
||||||
|
};
|
||||||
99
frontend/src/sections/overview/overview-latest-products.js
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ArrowRightIcon from '@heroicons/react/24/solid/ArrowRightIcon';
|
||||||
|
import EllipsisVerticalIcon from '@heroicons/react/24/solid/EllipsisVerticalIcon';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardActions,
|
||||||
|
CardHeader,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemAvatar,
|
||||||
|
ListItemText,
|
||||||
|
SvgIcon
|
||||||
|
} from '@mui/material';
|
||||||
|
|
||||||
|
export const OverviewLatestProducts = (props) => {
|
||||||
|
const { products = [], sx } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card sx={sx}>
|
||||||
|
<CardHeader title="Latest Products" />
|
||||||
|
<List>
|
||||||
|
{products.map((product, index) => {
|
||||||
|
const hasDivider = index < products.length - 1;
|
||||||
|
const ago = formatDistanceToNow(product.updatedAt);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
divider={hasDivider}
|
||||||
|
key={product.id}
|
||||||
|
>
|
||||||
|
<ListItemAvatar>
|
||||||
|
{
|
||||||
|
product.image
|
||||||
|
? (
|
||||||
|
<Box
|
||||||
|
component="img"
|
||||||
|
src={product.image}
|
||||||
|
sx={{
|
||||||
|
borderRadius: 1,
|
||||||
|
height: 48,
|
||||||
|
width: 48
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
borderRadius: 1,
|
||||||
|
backgroundColor: 'neutral.200',
|
||||||
|
height: 48,
|
||||||
|
width: 48
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</ListItemAvatar>
|
||||||
|
<ListItemText
|
||||||
|
primary={product.name}
|
||||||
|
primaryTypographyProps={{ variant: 'subtitle1' }}
|
||||||
|
secondary={`Updated ${ago} ago`}
|
||||||
|
secondaryTypographyProps={{ variant: 'body2' }}
|
||||||
|
/>
|
||||||
|
<IconButton edge="end">
|
||||||
|
<SvgIcon>
|
||||||
|
<EllipsisVerticalIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</IconButton>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
<Divider />
|
||||||
|
<CardActions sx={{ justifyContent: 'flex-end' }}>
|
||||||
|
<Button
|
||||||
|
color="inherit"
|
||||||
|
endIcon={(
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ArrowRightIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
)}
|
||||||
|
size="small"
|
||||||
|
variant="text"
|
||||||
|
>
|
||||||
|
View all
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
OverviewLatestProducts.propTypes = {
|
||||||
|
products: PropTypes.array,
|
||||||
|
sx: PropTypes.object
|
||||||
|
};
|
||||||