Merge branch 'release/v0.1.0'

This commit is contained in:
Amirhossein Mahmoodi
2024-07-10 11:57:34 +03:30
103 changed files with 3459 additions and 3293 deletions

2
.env.local.example Normal file
View File

@@ -0,0 +1,2 @@
NEXT_PUBLIC_VERSION="0.1.0"
NEXT_PUBLIC_API_URL="https://rms.witel.ir"

View File

@@ -1,3 +1,3 @@
{
"extends": "next/core-web-vitals"
"extends": ["next/core-web-vitals","prettier"]
}

2
.gitignore vendored
View File

@@ -27,7 +27,7 @@ node_modules
cypress/videos
cypress/screenshots
.next
.env*
.env.local
.env
.idea
package-lock.json

12
.prettierrc Normal file
View File

@@ -0,0 +1,12 @@
{
"printWidth": 120,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": false,
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "always",
"endOfLine": "crlf"
}

View File

@@ -1,485 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = crlf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 120
tab_width = 4
ij_continuation_indent_size = 4
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = true
ij_smart_tabs = false
ij_visual_guides = none
ij_wrap_on_typing = false
[*.css]
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_block_comment_add_space = false
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
[*.sass]
indent_size = 2
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_line_comment_add_space = false
ij_sass_line_comment_at_first_column = 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]
indent_size = 2
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_block_comment_add_space = false
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_line_comment_add_space = false
ij_scss_line_comment_at_first_column = 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
[{*.ats,*.cts,*.mts,*.ts}]
ij_continuation_indent_size = 4
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 = false
ij_typescript_align_multiline_extends_list = false
ij_typescript_align_multiline_for = true
ij_typescript_align_multiline_parameters = true
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 = false
ij_typescript_array_initializer_right_brace_on_new_line = false
ij_typescript_array_initializer_wrap = off
ij_typescript_assignment_wrap = off
ij_typescript_binary_operation_sign_on_next_line = false
ij_typescript_binary_operation_wrap = off
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_block_comment_add_space = false
ij_typescript_block_comment_at_first_column = true
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 = off
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 = never
ij_typescript_else_on_new_line = false
ij_typescript_enforce_trailing_comma = keep
ij_typescript_enum_constants_wrap = on_every_item
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 = never
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 = false
ij_typescript_force_semicolon_style = false
ij_typescript_function_expression_brace_style = end_of_line
ij_typescript_if_brace_force = never
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 = 2
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 = false
ij_typescript_keep_simple_methods_in_one_line = false
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 = off
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 = off
ij_typescript_object_literal_wrap = on_every_item
ij_typescript_object_types_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 = false
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 = false
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 = false
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 = false
ij_typescript_ternary_operation_wrap = off
ij_typescript_union_types_wrap = on_every_item
ij_typescript_use_chained_calls_group_indents = false
ij_typescript_use_double_quotes = true
ij_typescript_use_explicit_js_extension = auto
ij_typescript_use_path_mapping = always
ij_typescript_use_public_modifier = false
ij_typescript_use_semicolon_after_statement = true
ij_typescript_var_declaration_wrap = normal
ij_typescript_while_brace_force = never
ij_typescript_while_on_new_line = false
ij_typescript_wrap_comments = false
[{*.cjs,*.js}]
ij_continuation_indent_size = 4
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 = false
ij_javascript_align_multiline_extends_list = false
ij_javascript_align_multiline_for = true
ij_javascript_align_multiline_parameters = true
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 = false
ij_javascript_array_initializer_right_brace_on_new_line = false
ij_javascript_array_initializer_wrap = off
ij_javascript_assignment_wrap = off
ij_javascript_binary_operation_sign_on_next_line = false
ij_javascript_binary_operation_wrap = off
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_block_comment_add_space = false
ij_javascript_block_comment_at_first_column = true
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 = off
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 = never
ij_javascript_else_on_new_line = false
ij_javascript_enforce_trailing_comma = keep
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 = never
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 = false
ij_javascript_force_semicolon_style = false
ij_javascript_function_expression_brace_style = end_of_line
ij_javascript_if_brace_force = never
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 = 2
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 = false
ij_javascript_keep_simple_methods_in_one_line = false
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 = off
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 = off
ij_javascript_object_literal_wrap = on_every_item
ij_javascript_object_types_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 = false
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 = false
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 = false
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 = false
ij_javascript_ternary_operation_wrap = off
ij_javascript_union_types_wrap = on_every_item
ij_javascript_use_chained_calls_group_indents = false
ij_javascript_use_double_quotes = true
ij_javascript_use_explicit_js_extension = auto
ij_javascript_use_path_mapping = always
ij_javascript_use_public_modifier = false
ij_javascript_use_semicolon_after_statement = true
ij_javascript_var_declaration_wrap = normal
ij_javascript_while_brace_force = never
ij_javascript_while_on_new_line = false
ij_javascript_wrap_comments = false
[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}]
indent_size = 2
ij_json_array_wrapping = split_into_lines
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_keep_trailing_comma = false
ij_json_object_wrapping = split_into_lines
ij_json_property_alignment = do_not_align
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}]
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 = normal
ij_html_block_comment_add_space = false
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 = 2
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 = never
ij_html_new_line_before_first_attribute = never
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 = false
ij_html_text_wrap = normal
[{*.markdown,*.md}]
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_format_tables = true
ij_markdown_insert_quote_arrows_on_wrap = true
ij_markdown_keep_indents_on_empty_lines = false
ij_markdown_keep_line_breaks_inside_text_blocks = true
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
ij_markdown_wrap_text_if_long = true
ij_markdown_wrap_text_inside_blockquotes = true

View File

@@ -1,4 +1,8 @@
const isProd = process.env.NODE_ENV === 'production'
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
assetPrefix: isProd ? '/v3' : undefined,
};
export default nextConfig;

View File

@@ -6,7 +6,9 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,html,css,scss}\"",
"build:publish": "npm i && npm run build && pm2 restart ecosystem.config.js"
},
"dependencies": {
"@emotion/cache": "^11.11.0",
@@ -16,21 +18,27 @@
"@mui/material": "^5.15.6",
"@mui/material-nextjs": "^5.15.6",
"@mui/x-date-pickers": "^6.19.5",
"axios": "^1.7.2",
"date-fns": "^3.3.1",
"date-fns-jalali": "^2.13.0-0",
"dayjs": "^1.11.10",
"formik": "^2.4.5",
"jalali-moment": "^3.3.11",
"material-react-table": "^2.11.2",
"next": "14.1.0",
"next": "^14.2.3",
"react": "^18",
"react-dom": "^18",
"react-toastify": "^10.0.5",
"sass": "^1.75.0",
"stylis": "^4.3.1",
"stylis-plugin-rtl": "^2.1.1"
"stylis-plugin-rtl": "^2.1.1",
"swr": "^2.2.5",
"yup": "^1.4.0"
},
"devDependencies": {
"eslint": "^8",
"eslint-config-next": "14.1.0"
"eslint-config-next": "14.1.0",
"eslint-config-prettier": "^9.1.0",
"prettier": "^3.3.2"
}
}
}

View File

@@ -1 +1,9 @@
@import "fontiran";
@import "fontiran";
@import "react-toastify/dist/ReactToastify.css";
.filter-toast {
box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px,
rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
background-color: #d0dfe8;
border: 1px dashed #095b8c;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 468.85 509.45"><defs><style>.cls-1{fill:#ed8a23;}.cls-2{fill:#4962ad;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><polygon class="cls-1" points="122.85 98.5 122.85 154.2 133.75 143.8 192.85 183.7 192.85 98.3 122.85 98.5"/><polygon class="cls-1" points="212.05 56.5 211.35 193.7 241.55 213.2 285.05 180.5 285.55 55.7 212.05 56.5"/><polygon class="cls-1" points="304.45 0.8 304.85 167.5 374.45 114.9 374.35 0 304.45 0.8"/><path class="cls-1" d="M387.85,233.4A24,24,0,1,0,353,254.8a144.82,144.82,0,0,1-232.2,103.3,145.22,145.22,0,0,1-55.4-94.3l-8,6.7-12-14.3a163.44,163.44,0,0,0,56.3,110.9L98,371.4h0l-11.3,14.7A11.71,11.71,0,0,0,71,388.7L4.45,475a21.39,21.39,0,1,0,33.9,26.1l66.5-86.3a11.87,11.87,0,0,0-1-15.5l11.5-14.9h0l3.1-4.7a163.17,163.17,0,0,0,90.1,27.1h0a163.57,163.57,0,0,0,163.5-150.9A23.78,23.78,0,0,0,387.85,233.4Z"/><polygon class="cls-2" points="468.85 83.1 383.35 93.6 400.45 116.3 241.55 235 133.55 163.9 38.95 240.2 40.85 242.4 57.95 262.7 69.75 252.9 133.95 198.7 241.55 271 417.55 139 435.15 162.4 468.85 83.1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,7 +0,0 @@
const Layout = ({children}) => {
return (
<>{children}</>
)
}
export default Layout

View File

@@ -1,7 +1,11 @@
const Page = () => {
return (
<div>dashboard</div>
)
}
"use client";
export default Page
const Page = () => {
return (
<>
</>
);
};
export default Page;

View File

@@ -1,16 +1,17 @@
import {Box, Stack} from "@mui/material";
import { Box, Stack } from "@mui/material";
import HeaderWithLogo from "@/components/layouts/dashboard/headerWithLogo";
import HeaderWithSidebar from "@/components/layouts/dashboard/headerWithSidebar";
import "react-toastify/dist/ReactToastify.css";
const Template = ({children}) => {
return (<Stack>
<HeaderWithLogo/>
<Box>
<HeaderWithSidebar>
{children}
</HeaderWithSidebar>
</Box>
</Stack>)
}
const Template = ({ children }) => {
return (
<Stack>
<HeaderWithLogo />
<Box>
<HeaderWithSidebar>{children}</HeaderWithSidebar>
</Box>
</Stack>
);
};
export default Template
export default Template;

View File

@@ -0,0 +1,12 @@
import WithAuthMiddleware from "@/core/middlewares/withAuth";
import { AuthProvider } from "@/lib/contexts/auth";
const Layout = ({ children }) => {
return (
<AuthProvider>
<WithAuthMiddleware>{children}</WithAuthMiddleware>
</AuthProvider>
);
};
export default Layout;

3
src/app/default.js Normal file
View File

@@ -0,0 +1,3 @@
export default function Default() {
return null;
}

View File

@@ -1,17 +1,15 @@
import {TableSettingProvider} from "@/lib/contexts/tableSetting";
import { TableSettingProvider } from "@/lib/contexts/tableSetting";
export const metadata = {
title: 'سامانه جامع راهداری',
}
title: "سامانه جامع راهداری",
};
export default function RootLayout({children}) {
export default function RootLayout({ children }) {
return (
<html lang="fa" dir={'rtl'}>
<body style={{height: '100vh'}}>
<TableSettingProvider>
{children}
</TableSettingProvider>
</body>
<html lang="fa" dir={"rtl"}>
<body style={{ height: "100vh" }}>
<TableSettingProvider>{children}</TableSettingProvider>
</body>
</html>
)
);
}

57
src/app/lfwd/page.js Normal file
View File

@@ -0,0 +1,57 @@
"use client";
import { GET_LOGIN_ROUTE } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
import { Button, Stack, Typography, Zoom } from "@mui/material";
import Link from "next/link";
import { useEffect, useState } from "react";
const Page = ({ searchParams }) => {
const { username } = searchParams;
const [login, setLogin] = useState(0);
const request = useRequest();
useEffect(() => {
const login = async () => {
try {
await request(`${GET_LOGIN_ROUTE}?username=${username}`);
setLogin(1);
} catch (error) {
setLogin(2);
}
};
login();
}, []);
const retry = async () => {
setLogin(0);
try {
await request(`${GET_LOGIN_ROUTE}?username=${username}`);
setLogin(1);
} catch (error) {
setLogin(2);
}
};
return (
<Stack alignItems={"center"} justifyContent={"center"} sx={{ height: "100%" }} spacing={2}>
<Typography variant="h5">
{login === 0
? "درحال دریافت مجوز برای ارتباط با سرور..."
: login === 1
? "ارتباط با سرور برقرار شد."
: "ارتباط با سرور برقرار نشد. مشکلی وجود دارد."}
</Typography>
<Zoom in={login === 1}>
<Button component={Link} href="/">
ورود به سامانه
</Button>
</Zoom>
<Zoom in={login === 2}>
<Button onClick={retry}>تلاش مجدد</Button>
</Zoom>
</Stack>
);
};
export default Page;

View File

@@ -1,148 +1,8 @@
'use client'
import DataTable from "@/core/components/DataTable";
import { useMemo } from "react";
import { redirect } from "next/navigation";
function Page() {
const columns = useMemo(
() => [
{
accessorKey: 'fullname',
header: 'نام کامل',
id: "fullname",
columns: [
{
accessorKey: 'name.firstName',
header: 'نام',
id: "firstName",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
enableClickToCopy: true,
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'name.lastName',
header: 'نام خانوادگی',
id: "lastName",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
],
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'address',
header: 'آدرس',
id: "address",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'city',
header: 'شهر',
id: "city",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
},
{
accessorKey: 'state',
header: 'وضعیت',
id: "state",
text: 'نام',
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'state',
header: 'محدوده',
id: "betweenNum",
text: 'محدوده',
enableColumnFilter: true,
datatype: "text",
filterFn: "betweenNum",
columnFilterModeOptions: [],
},
{
accessorKey: 'state',
header: 'تاریخ',
id: "betweenDate",
text: 'تاریخ',
enableColumnFilter: true,
datatype: "text",
filterFn: "betweenDate",
columnFilterModeOptions: [],
},
{
accessorKey: 'state',
header: 'شلکت',
id: "select2",
text: 'تاریخ',
type: 'select2',
enableColumnFilter: true,
datatype: "text",
filterFn: "equals",
columnFilterModeOptions: [],
},
{
accessorKey: 'state',
header: 'سلکت',
id: "select",
text: 'تنتنت',
type: 'select',
enableColumnFilter: true,
datatype: "text",
filterFn: "equals",
columnFilterModeOptions: [],
},
],
[],
);
return (
<>
<DataTable columns={columns} userId={0} pageName={'testPage'} tableName={'testTable'} />
</>
);
redirect("/dashboard", "replace");
return null;
}
export default Page;

View File

@@ -1,18 +1,21 @@
import cacheProviderRtl from "@/core/utils/cacheRtl";
import {CssBaseline, ThemeProvider} from "@mui/material";
import { CssBaseline, ThemeProvider } from "@mui/material";
import theme from "@/core/utils/theme";
import {AppRouterCacheProvider} from "@mui/material-nextjs/v14-appRouter";
import "^/global.scss"
import { AppRouterCacheProvider } from "@mui/material-nextjs/v14-appRouter";
import "^/global.scss";
import { ToastContainer } from "react-toastify";
const Template = ({children}) => {
const Template = ({ children }) => {
return (
<AppRouterCacheProvider CacheProvider={cacheProviderRtl} options={{enableCssLayer: true}}>
<AppRouterCacheProvider CacheProvider={cacheProviderRtl} options={{ enableCssLayer: true }}>
<ThemeProvider theme={theme}>
<CssBaseline/>
<CssBaseline />
{children}
<ToastContainer rtl containerId="filtering" closeButton={false} />
<ToastContainer rtl containerId="request_data" />
</ThemeProvider>
</AppRouterCacheProvider>
)
}
);
};
export default Template
export default Template;

View File

@@ -1,31 +0,0 @@
import {Grid} from '@mui/material';
import MuiDatePicker from "@/components/MuiDatePicker";
import {useEffect, useState} from "react";
const BetWeenDateFilter = ({formik, id , defaultValue}) => {
useEffect(() => {
formik.setFieldValue(`${id}[0]`, defaultValue?.[0] || '');
formik.setFieldValue(`${id}[1]`, defaultValue?.[1] || '');
}, []);
const [dateRange, setDateRange] = useState(defaultValue);
const handleDateChange = (index, value) => {
const newRange = [...dateRange];
newRange[index] = value;
setDateRange(newRange);
};
return (
<Grid container spacing={1} mt={1} mb={2}>
<Grid item xs={6}>
<MuiDatePicker label="تاریخ شروع " name={`${id}[0]`} formik={formik} defaultValue={defaultValue?.[0]} minDate={null} maxDate={dateRange[1]} onDateChange={(value) => handleDateChange(0, value)}/>
</Grid>
<Grid item xs={6}>
<MuiDatePicker label="تاریخ پایان" name={`${id}[1]`} formik={formik} defaultValue={defaultValue?.[1]} minDate={dateRange[0]} maxDate={null} onDateChange={(value) => handleDateChange(1, value)}/>
</Grid>
</Grid>
);
};
export default BetWeenDateFilter;

View File

@@ -1,58 +0,0 @@
import {Grid, TextField} from '@mui/material';
import {useEffect} from "react";
const BetweenNumberFilter = ({formik, id , defaultValue}) => {
useEffect(() => {
formik.setFieldValue(`${id}[0]`, defaultValue?.[0] || '');
formik.setFieldValue(`${id}[1]`, defaultValue?.[1] || '');
}, []);
const handleBlur = (index) => () => {
const minValue = formik.values[id][0];
const maxValue = formik.values[id][1];
if (index === 0 && minValue !== '' && maxValue !== '' && parseInt(minValue) > parseInt(maxValue)) {
formik.setFieldValue(`${id}[0]`, maxValue);
}
if (index === 1 && minValue !== '' && maxValue !== '' && parseInt(maxValue) < parseInt(minValue)) {
formik.setFieldValue(`${id}[1]`, minValue);
}
};
return (
<Grid container spacing={2}>
<Grid item xs={6}>
<TextField
name={`${id}[0]`}
label="کمترین رقم"
id={`${id}_min`}
onChange={formik.handleChange}
onBlur={handleBlur(0)}
fullWidth
value={formik.values[id][0] || ''}
margin="normal"
sx={{
position: 'relative'
}}
/>
</Grid>
<Grid item xs={6}>
<TextField
name={`${id}[1]`}
label="بیشترین رقم"
id={`${id}_max`}
onChange={formik.handleChange}
onBlur={handleBlur(1)}
fullWidth
value={formik.values[id][1] || ''}
margin="normal"
sx={{
position: 'relative'
}}
/>
</Grid>
</Grid>
);
};
export default BetweenNumberFilter;

View File

@@ -1,20 +0,0 @@
import FilterColumn from "@/components/FilterColumn";
const CustomToolbar = ({columns}) => {
return (
<FilterColumn columns={columns}/>
)
}
export default CustomToolbar

View File

@@ -1,141 +0,0 @@
import DataTableTest from "@/components/DataTableTest";
import FilterColumn from "@/components/FilterColumn";
import {useMemo} from "react";
import {Button} from "@mui/material";
function DataTableMain() {
const columns = useMemo(
() => [
{
accessorKey: 'name.firstName',
header: 'نام',
size: 150,
id: "firstName",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'name.lastName',
header: 'نام خانوادگی',
size: 150,
id: "lastName",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'address',
header: 'آدرس',
size: 200,
id: "address",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'city',
header: 'شهر',
size: 150,
id: "city",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
},
{
accessorKey: 'state',
header: 'وضعیت',
size: 150,
id: "state",
text:'نام',
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'state',
header: 'محدوده',
size: 150,
id: "betweenNum",
text:'محدوده',
enableColumnFilter: true,
datatype: "text",
filterFn: "betweenNum",
columnFilterModeOptions: [],
},
{
accessorKey: 'state',
header: 'تاریخ',
size: 150,
id: "betweenDate",
text:'تاریخ',
enableColumnFilter: true,
datatype: "text",
filterFn: "betweenDate",
columnFilterModeOptions: [],
},
{
accessorKey: 'state',
header: 'شلکت',
size: 150,
id: "select2",
text:'تاریخ',
type: 'select2',
enableColumnFilter: true,
datatype: "text",
filterFn: "equals",
columnFilterModeOptions: [],
},
{
accessorKey: 'state',
header: 'سلکت',
size: 150,
id: "select",
text:'تنتنت',
type: 'select',
enableColumnFilter: true,
datatype: "text",
filterFn: "equals",
columnFilterModeOptions: [],
},
],
[],
);
return (
<>
<DataTableTest columns={columns}/>
</>
);
}
export default DataTableMain;

View File

@@ -1,104 +0,0 @@
"use client"
import {useMemo, useState} from 'react';
import {MaterialReactTable} from 'material-react-table';
import useTableSetting from "@/lib/hooks/useTableSetting";
import {IconButton, Tooltip, Typography} from "@mui/material";
import CustomToolbar from "@/components/CustomToolbar";
const data = [
{
name: {
firstName: 'John',
lastName: 'Doe',
},
address: '261 Erdman Ford',
city: 'East Daphne',
state: 'Kentucky',
},
{
name: {
firstName: 'Jane',
lastName: 'Doe',
},
address: '769 Dominic Grove',
city: 'Columbus',
state: 'Ohio',
},
{
name: {
firstName: 'Joe',
lastName: 'Doe',
},
address: '566 Brakus Inlet',
city: 'South Linda',
state: 'West Virginia',
},
{
name: {
firstName: 'Kevin',
lastName: 'Vandy',
},
address: '722 Emie Stream',
city: 'Lincoln',
state: 'Nebraska',
},
{
name: {
firstName: 'Joshua',
lastName: 'Rolluffs',
},
address: '32188 Larkin Turnpike',
city: 'Charleston',
state: 'South Carolina',
},
];
function RefreshIcon() {
return null;
}
const TestDataTable = ({columns}) => {
const [userId, setUserId] = useState(2);
const [pageName, setPageName] = useState('loan');
const [tableName, setTableName] = useState('loan_table');
const {settingStore, hideAction, filterAction, sortAction,} = useTableSetting();
const onColumnVisibilityChange = (event) => {
const settingValue = event();
hideAction(userId, pageName, tableName, settingValue, columns);
};
const onSortingChange = (event) => {
const settingValue = event(settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []);
sortAction(userId, pageName, tableName, settingValue, columns);
}
return (
<>
<Typography>{settingStore?.[userId]?.[pageName]?.[tableName]?.['summary']}</Typography>
<MaterialReactTable
columns={columns}
data={data}
manualSorting={true}
renderTopToolbarCustomActions={({table}) => (<>
<CustomToolbar
columns={columns}
/>
</>)}
initialState={{
columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
}}
state={{
columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
}}
onColumnVisibilityChange={onColumnVisibilityChange}
onSortingChange={onSortingChange}
/>
</>
);
};
export default TestDataTable;

View File

@@ -1,30 +0,0 @@
import { Popper, Paper, List, ListItem, ListItemText, ClickAwayListener } from '@mui/material';
function FilterBox({anchorEl , open = false, handleClose = () => {} , columnFilterModeOptions , onSelectFilter , defaultFilter}) {
const id = open ? 'simple-popper' : undefined;
const handleSelectFilter = (filterFn) => {
onSelectFilter(filterFn);
handleClose();
};
return (
<div>
<Popper id={id} open={open} anchorEl={anchorEl} placement="bottom" style={{ zIndex: 1301 }}>
<ClickAwayListener onClickAway={handleClose}>
<Paper>
<List>
{columnFilterModeOptions.map((option, index) => (
<ListItem key={index} onClick={() => handleSelectFilter(option)} selected={option === defaultFilter} sx={{ cursor: 'pointer' }}>
<ListItemText primary={option} />
</ListItem>
))}
</List>
</Paper>
</ClickAwayListener>
</Popper>
</div>
);
}
export default FilterBox;

View File

@@ -1,204 +0,0 @@
"use client"
import {
Box,
Button, Divider, Drawer,
FormControl, IconButton, styled, Typography
} from '@mui/material';
import {useFormik} from 'formik';
import TextFieldWithFnBox from "@/components/TextFieldWithFnBox";
import {useEffect, useState} from "react";
import useTableSetting from "@/lib/hooks/useTableSetting";
import BetweenNumberFilter from "@/components/BetweenNumberFilter";
import BetWeenDateFilter from "@/components/BetweenDateFilter";
import SelectBoxMultiple from "@/components/SelectBoxMultiple";
import SelectBox from "@/components/SelectBox";
import CloseIcon from '@mui/icons-material/Close';
import { useTheme } from '@mui/material/styles';
function FilterColumn({ columns }) {
const [open, setOpen] = useState(false);
const [selectedFilters, setSelectedFilters] = useState([]);
const [userId, setUserId] = useState(2);
const [pageName, setPageName] = useState('loan');
const [tableName, setTableName] = useState('loan_table');
const {settingStore, hideAction, filterAction, sortAction,} = useTableSetting();
const DrawerHeader = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
padding: theme.spacing(0, 1),
justifyContent: 'space-between',
}));
const toggleDrawer = (newOpen) => () => {
setOpen(newOpen);
};
const handleDrawerClose = () => {
setOpen(false);
};
useEffect(() => {
const settingValue = settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || [];
setSelectedFilters(settingValue.length > 0 ? settingValue : []);
}, [settingStore]);
const handleSelectFilter = (fieldName, filterFn) => {
const filterIndex = selectedFilters.findIndex(filter => filter.id === fieldName);
if (filterIndex !== -1) {
const updatedFilters = [...selectedFilters];
updatedFilters[filterIndex].fn = filterFn;
setSelectedFilters(updatedFilters);
} else {
const newFilter = { id: fieldName, fn: filterFn };
const updatedFilters = [...selectedFilters, newFilter];
setSelectedFilters(updatedFilters);
}
};
const initialValues = columns.reduce((acc, column) => {
if (column.enableColumnFilter) {
const filter = settingStore?.[userId]?.[pageName]?.[tableName]?.['filters']?.find((filter) => filter.id === column.id);
if (column.filterFn === 'betweenNum' || column.filterFn === 'betweenDate') {
acc[column.id] = filter?.value || ["", ""];
}
else if ( column.type === 'select2' ) {
acc[column.id] = filter?.value || [];
}
else {
acc[column.id] = filter?.value || '';
}
}
return acc;
}, {});
const formik = useFormik({
initialValues,
onSubmit: (values) => {
const updatedFilters = selectedFilters.map(filter => {
const value = values[filter.id];
if (value !== undefined) {
return { ...filter, value };
}
return filter;
});
Object.entries(values).forEach(([fieldName, value]) => {
const filterIndex = updatedFilters.findIndex(filter => filter.id === fieldName);
if (filterIndex === -1 && value !== '' && value !== null && value !== undefined) {
const column = columns.find(col => col.id === fieldName);
updatedFilters.push({ id: fieldName, value, fn: column.filterFn, datatype: column.datatype });
}
});
const newArrayFilter = updatedFilters.filter((filter) => {
const value = values[filter.id];
return (
value !== '' &&
value !== null &&
value !== undefined &&
!(Array.isArray(value) && (value.length === 0 || (value.length === 2 && value.every((v) => v === ''))))
);
});
setSelectedFilters(newArrayFilter);
filterAction(userId, pageName, tableName, newArrayFilter, columns);
}
});
return (
<>
<Button onClick={toggleDrawer(true)}>Open drawer</Button>
<Drawer open={open} onClose={toggleDrawer(false)}
sx={{
"& .MuiDrawer-paper": {
padding: '16px',
overflowY: 'auto',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
scrollbarWidth: 'thin',
width:450,
"&::-webkit-scrollbar": {
width: '8px',
},
"&::-webkit-scrollbar-thumb": {
backgroundColor: '#ccc',
borderRadius: '4px',
},
"@media (max-width: 600px)": {
width: '100%'
},
}
}}
>
<DrawerHeader>
<Typography variant="h6" mb={2}>فیلتر</Typography>
<IconButton onClick={handleDrawerClose}>
<CloseIcon/>
</IconButton>
</DrawerHeader>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<FormControl fullWidth>
{columns.map((column, index) => (
column.enableColumnFilter &&
(column.filterFn === 'betweenDate' ?
<BetWeenDateFilter
key={index}
id={column.id}
formik={formik}
onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
/>
: column.filterFn === 'betweenNum' ?
<BetweenNumberFilter
key={index}
id={column.id}
formik={formik}
onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ["", ""]}
/>
: column.type === 'select2' ?
<SelectBoxMultiple
key={index}
id={column.id}
formik={formik}
onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || []}
/>
: column.type === 'select' ?
<SelectBox
key={index}
id={column.id}
formik={formik}
onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
/>
:
<TextFieldWithFnBox
key={index}
id={column.id}
header={column.header}
formik={formik}
defaultFilter={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.fn || column.filterFn}
columnFilterModeOptions={column.columnFilterModeOptions}
onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
defaultValue={formik.values[column.id] || (settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
/>
)
))}
</FormControl>
</Box>
<Box sx={{ mt:2 }}>
<Divider />
<Box sx={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}>
<Button variant="contained" size="medium" onClick={formik.handleSubmit} sx={{ mt: 1 }}>ثبت</Button>
</Box>
</Box>
</Drawer>
</>
);
}
export default FilterColumn;

View File

@@ -1,41 +0,0 @@
"use client"
import {Typography} from "@mui/material";
const data = [
{
id : "hello",
Subitems : [
{id : "amin",firstName : "amin", lastName:"ali",Subitems : [{id : "shahrokh",shahrokh : "shahrokh"}], hasSubitems : true, showSubitems : false},
{id : "amir",firstName : "amir", lastName:"akbar", hasSubitems : false, showSubitems : false},
],
hasSubitems : true,
showSubitems : false
},
{
id : "hi",
Subitems : [
{id : "ali",firstName : "ali", lastName:"ali", hasSubitems : false, showSubitems : false},
{id : "akbar",firstName : "akbar", lastName:"akbar", hasSubitems : false, showSubitems : false},
],
hasSubitems : true,
showSubitems : false
}
]
const MapTest = () => {
const Amin = data.map((item) => {
return item.hasSubitems ? {...item, Subitems: item.Subitems.map((subitem) => true ? {
...subitem, showSubitems: !item.showSubitems
} : subitem)} : item
})
console.log(Amin)
return (
<>
<Typography>hello</Typography>
</>
)
}
export default MapTest

View File

@@ -1,53 +0,0 @@
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import {useEffect, useState} from "react";
const names = [
'Oliver Hansen',
'Van Henry',
'April Tucker',
'Ralph Hubbard',
'Omar Alexander',
'Carlos Abbott',
'Miriam Wagner',
'Bradley Wilkerson',
'Virginia Andrews',
'Kelly Snyder',
];
const SelectBox = ({ formik, id, defaultValue }) => {
useEffect(() => {
formik.setFieldValue(id, defaultValue || '');
}, []);
const [selectedValue, setSelectedValue] = useState(defaultValue || '');
const handleChange = (event) => {
const { value } = event.target;
setSelectedValue(value);
formik.setFieldValue(id, value);
};
return (
<>
<FormControl sx={{ mt: 3 }}>
<InputLabel>انتخاب</InputLabel>
<Select
id={id}
value={selectedValue}
onChange={handleChange}
label="انتخاب"
>
{names.map((name) => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</>
);
};
export default SelectBox;

View File

@@ -1,116 +0,0 @@
import {useTheme} from '@mui/material/styles';
import Box from '@mui/material/Box';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import Chip from '@mui/material/Chip';
import {useEffect, useState} from "react";
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
const names = [
'Oliver Hansen',
'Van Henry',
'April Tucker',
'Ralph Hubbard',
'Omar Alexander',
'Carlos Abbott',
'Miriam Wagner',
'Bradley Wilkerson',
'Virginia Andrews',
'Kelly Snyder',
];
function getStyles(name, personName, theme) {
return {
fontWeight:
personName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
const SelectBoxMultiple = ({formik, id, defaultValue}) => {
const theme = useTheme();
const [personName, setPersonName] = useState(defaultValue || []);
useEffect(() => {
defaultValue.forEach((value, index) => {
formik.setFieldValue(`${id}[${index}]`, value || '');
});
}, []);
const handleChange = (event) => {
const {
target: {value},
} = event;
const selectedValues = typeof value === 'string' ? value.split(',').filter(item => item.trim() !== '') : value;
formik.setFieldValue(id, []);
if (selectedValues.length === 0) {
setPersonName([]);
formik.setFieldValue(id, []);
} else {
setPersonName(selectedValues);
selectedValues.forEach((selectedValue, index) => {
formik.setFieldValue(`${id}[${index}]`, selectedValue);
});
}
};
return (
<>
<FormControl sx={{width: '100%'}}>
<InputLabel id={id}>انتخاب</InputLabel>
<Select
id={id}
multiple
value={personName}
onChange={handleChange}
input={<OutlinedInput label="Chip"/>}
renderValue={(selected) => (
<Box sx={{display: 'flex', flexWrap: 'wrap', gap: 0.5}}>
{selected.map((value) => (
<Chip key={value} label={value}/>
))}
</Box>
)}
MenuProps={MenuProps}
sx={{
"&.MuiSelect-select": {
minWidth: 0,
maxWidth: '100%',
overflow: 'hidden',
},
}}
>
{names.map((name) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, personName, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</>
);
};
export default SelectBoxMultiple;

View File

@@ -1,51 +0,0 @@
import {IconButton, Paper, TextField} from '@mui/material';
import ArtTrackIcon from '@mui/icons-material/ArtTrack';
import {useEffect, useState} from "react";
import FilterBox from "@/components/FilterBox";
const TextFieldWithFnBox = ({formik, id , header, defaultValue, defaultFilter , columnFilterModeOptions ,onSelectFilter}) => {
const [anchorEl, setAnchorEl] = useState(null);
const [open, setOpen] = useState(false);
useEffect(() => {
formik.setFieldValue(id, defaultValue || '');
}, []);
const handleClick = (event) => {
setOpen((prevOpen) => !prevOpen);
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const handleClose = () => {
setOpen(false);
setAnchorEl(null);
};
return (
<>
<TextField
name={id}
label={header}
id={id}
onChange={formik.handleChange}
fullWidth
value={formik.values[id]}
margin="normal"
InputProps={{
endAdornment: (
<IconButton onClick={handleClick}>
<ArtTrackIcon />
</IconButton>
)
}}
sx={{
position: 'relative'
}}
/>
<FilterBox anchorEl={anchorEl} open={open} handleClose={handleClose} columnFilterModeOptions={columnFilterModeOptions} onSelectFilter={onSelectFilter} defaultFilter={defaultFilter}/>
</>
);
};
export default TextFieldWithFnBox;

View File

@@ -0,0 +1,10 @@
import { Button } from "@mui/material";
const Toolbar = ({ columns }) => {
return (
<>
<Button>add something</Button>
</>
);
};
export default Toolbar;

View File

@@ -0,0 +1,129 @@
"use client";
import DataTable from "@/core/components/DataTable";
import { useMemo } from "react";
function HomeComponent() {
const columns = useMemo(
() => [
{
accessorKey: "fullname",
header: "نام کامل",
id: "fullname",
columns: [
{
accessorKey: "name.firstName",
header: "نام",
id: "firstName",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
enableClickToCopy: true,
columnFilterModeOptions: ["equals", "notEquals", "contains"],
},
{
accessorKey: "name.lastName",
header: "نام خانوادگی",
id: "lastName",
enableColumnFilter: true,
datatype: "text",
filterFn: "equals",
columnFilterModeOptions: ["equals", "notEquals", "contains"],
},
],
},
{
accessorKey: "address",
header: "آدرس",
id: "address",
enableColumnFilter: true,
datatype: "text",
filterFn: "notEquals",
columnFilterModeOptions: ["equals", "notEquals", "contains"],
},
{
accessorKey: "city",
header: "شهر",
id: "city",
enableColumnFilter: true,
datatype: "text",
filterFn: "equals",
columnFilterModeOptions: ["equals", "notEquals", "contains", "lessThan", "greaterThan", "fuzzy"],
},
{
accessorKey: "state",
header: "وضعیت",
id: "state",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: ["equals", "notEquals", "contains"],
},
{
accessorKey: "price",
header: "محدوده",
id: "price",
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnFilterModeOptions: ["equals", "notEquals"],
},
{
accessorKey: "created_at",
header: "تاریخ ساخت",
id: "created_at",
enableColumnFilter: true,
datatype: "date",
filterFn: "between",
},
{
accessorKey: "updated_at",
header: "تاریخ بروزرسانی",
id: "updated_at",
enableColumnFilter: true,
datatype: "date",
filterFn: "equals",
},
{
accessorKey: "company",
header: "نام شرکت",
id: "company",
enableColumnFilter: true,
datatype: "numeric",
filterFn: "equals",
columnSelectOption: [
{ value: 1, label: "وایتل" },
{ value: 2, label: "اسنپ" },
{ value: 3, label: "تپسی" },
],
},
{
accessorKey: "area",
header: "منطقه",
id: "area",
enableColumnFilter: true,
datatype: "array",
filterFn: "equals",
columnSelectOption: [
{ value: 1, label: "وایتل" },
{ value: 2, label: "اسنپ" },
{ value: 3, label: "تپسی" },
],
},
],
[]
);
return (
<>
<DataTable
columns={columns}
table_url={"api"}
user_id={0}
page_name={"testPage"}
table_name={"testTable"}
/>
</>
);
}
export default HomeComponent;

View File

@@ -0,0 +1,27 @@
"use client";
import { Stack, Typography } from "@mui/material";
import Image from "next/image";
const HeaderBottom = () => {
return (
<Stack
sx={{
px: 2,
py: 1,
background: (theme) => theme.palette.primary2.main,
}}
spacing={4}
alignItems={"center"}
direction={"row"}
>
<Image alt="" priority src={"/images/rmsLogo.svg"} width={65} height={71} />
<Stack alignItems={"center"} spacing={1}>
<Typography variant="h5" sx={{ color: (theme) => theme.palette.primary2.contrastText }}>
سامانه جامع راهداری
</Typography>
<Typography sx={{ color: (theme) => theme.palette.primary2.contrastText }}>rms.rmto.ir</Typography>
</Stack>
</Stack>
);
};
export default HeaderBottom;

View File

@@ -0,0 +1,11 @@
import { Stack } from "@mui/material";
import Image from "next/image";
const HeaderTop = () => {
return (
<Stack sx={{ p: 2 }}>
<Image alt="" priority src={"/images/headerLogo.png"} width={190} height={44} />
</Stack>
);
};
export default HeaderTop;

View File

@@ -1,9 +1,13 @@
import {Box, Stack} from "@mui/material";
import { Box, Stack } from "@mui/material";
import HeaderTop from "./HaederTop";
import HeaderBottom from "./HaederBottom";
const HeaderWithLogo = () => {
return (<Stack>
<Box>header1</Box>
<Box>header2</Box>
</Stack>)
}
export default HeaderWithLogo
return (
<Stack>
<HeaderTop />
<HeaderBottom />
</Stack>
);
};
export default HeaderWithLogo;

View File

@@ -0,0 +1,4 @@
const HeaderMenu = () => {
return <>منو</>;
};
export default HeaderMenu;

View File

@@ -1,94 +1,110 @@
'use client'
import {useState} from "react";
import {Box, Drawer, IconButton, Paper, styled, Toolbar, Typography} from "@mui/material";
import MuiAppBar from '@mui/material/AppBar';
import MenuIcon from '@mui/icons-material/Menu';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
"use client";
import SidebarMenu from "@/core/components/SidebarMenu";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import MenuIcon from "@mui/icons-material/Menu";
import { Box, Drawer, IconButton, Paper, styled, Toolbar } from "@mui/material";
import MuiAppBar from "@mui/material/AppBar";
import { useState } from "react";
import HeaderMenu from "./HeaderMenu";
const drawerWidth = 260;
const drawerWidth = 300;
const Main = styled(Paper, {shouldForwardProp: (prop) => prop !== 'open'})(({theme, open}) => ({
flexGrow: 1, transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen,
}), marginLeft: `-${drawerWidth}px`, ...(open && {
transition: theme.transitions.create('margin', {
easing: theme.transitions.easing.easeOut, duration: theme.transitions.duration.enteringScreen,
}), marginLeft: 0,
const Main = styled(Paper, { shouldForwardProp: (prop) => prop !== "open" })(({ theme, open }) => ({
flexGrow: 1,
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
}),);
marginLeft: `-${drawerWidth}px`,
...(open && {
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginLeft: 0,
}),
}));
const AppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})(({theme, open}) => ({
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen,
}), ...(open && {
shouldForwardProp: (prop) => prop !== "open",
})(({ theme, open }) => ({
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: `${drawerWidth}px`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut, duration: theme.transitions.duration.enteringScreen,
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}));
const DrawerHeader = styled('div')(({theme}) => ({
display: 'flex', alignItems: 'center', padding: theme.spacing(0, 1), // necessary for content to be below app bar
...theme.mixins.toolbar, justifyContent: 'flex-end',
const DrawerHeader = styled("div")(({ theme }) => ({
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1),
...theme.mixins.toolbar,
justifyContent: "flex-end",
}));
const HeaderWithSidebar = ({children}) => {
const [open, setOpen] = useState(false);
const HeaderWithSidebar = ({ children }) => {
const [open, setOpen] = useState(true);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (<Box sx={{display: 'flex'}}>
<AppBar sx={{position: 'fixed', top: "unset"}} elevation={0} open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={{mr: 2, ...(open && {display: 'none'})}}
>
<MenuIcon/>
</IconButton>
<Typography variant="h6" noWrap component="div">
menu
</Typography>
</Toolbar>
</AppBar>
<Drawer
sx={{
width: drawerWidth, flexShrink: 0, '& .MuiDrawer-paper': {
return (
<Box sx={{ display: "flex" }}>
<AppBar sx={{ position: "fixed", top: "unset" }} elevation={0} open={open}>
<Toolbar variant="dense">
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={{ mr: 2, ...(open && { display: "none" }) }}
>
<MenuIcon />
</IconButton>
<Box sx={{ flexGrow: 1, display: { xs: "none", md: "flex" } }}>
<HeaderMenu />
</Box>
</Toolbar>
</AppBar>
<Drawer
sx={{
width: drawerWidth,
boxSizing: 'border-box',
borderTop: 1,
borderTopColor: 'divider',
top: "unset"
},
}}
variant="persistent"
anchor="left"
open={open}
>
<DrawerHeader>
<IconButton onClick={handleDrawerClose}>
<ChevronRightIcon/>
</IconButton>
</DrawerHeader>
<SidebarMenu />
</Drawer>
<Main elevation={0} open={open}>
<DrawerHeader/>
{children}
</Main>
</Box>)
}
export default HeaderWithSidebar
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box",
borderTop: 1,
borderTopColor: "divider",
top: "unset",
},
}}
variant="persistent"
anchor="left"
open={open}
>
<DrawerHeader>
<IconButton onClick={handleDrawerClose}>
<ChevronRightIcon />
</IconButton>
</DrawerHeader>
<SidebarMenu />
</Drawer>
<Main elevation={0} open={open}>
<DrawerHeader />
{children}
</Main>
</Box>
);
};
export default HeaderWithSidebar;

View File

@@ -1,19 +1,183 @@
import { useMaterialReactTable } from "material-react-table";
import DataTable_Paper from "./table/Paper";
import { useMaterialReactTable } from "material-react-table";
import { FA_DATATABLE_LOCALIZATION } from "./localization/fa/datatable";
import useTableSetting from "@/lib/hooks/useTableSetting";
import { flattenArrayOfObjects } from "@/core/utils/flattenArrayOfObjects";
import Toolbar from "@/components/home/Toolbar";
import useDataTable from "@/lib/hooks/useDataTable";
import { useMemo, useState } from "react";
const isTableInstanceProp = (props) => props.table !== undefined;
const data = [
{
name: {
firstName: "امین",
lastName: "قاسمپور",
},
address: "فلان جا",
city: "تهران",
state: "بررسی شده",
price: "20",
updated_at: "2024/05/02",
company: "همراه اول",
area: "منطقه 5",
},
{
name: {
firstName: "امیر حسین",
lastName: "محمودی",
},
address: "فلان جا",
city: "تهران",
state: "تکمیل شده",
price: "27",
updated_at: "2024/05/02",
company: "وایتل",
area: "منطقه 27",
},
{
name: {
firstName: "یاسمن",
lastName: "علی اکبری",
},
address: "فلان جا",
city: "شیراز",
state: "بررسی شده",
price: "32",
updated_at: "2024/05/02",
company: "ایرانسل",
area: "منطقه 23",
},
{
name: {
firstName: "حمیدرضا",
lastName: "رنجبرپور",
},
address: "فلان جا",
city: "اصفهان",
state: "بررسی شده",
price: "45",
updated_at: "2024/05/02",
company: "ایرانول",
area: "منطقه 21",
},
{
name: {
firstName: "محمد",
lastName: "جلالی",
},
address: "فلان جا",
city: "رشت",
state: "درحال بررسی",
price: "8",
updated_at: "2024/05/02",
company: "فیلیمو",
area: "منطقه 8",
},
];
const DataTable_Main = (props) => {
let table;
const { filterData } = useDataTable();
const { table_url, user_id, page_name, table_name, columns, initialStateProps } = props;
const flatColumns = flattenArrayOfObjects(columns, "columns");
const { settingStore, hideAction, sortAction } = useTableSetting();
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
const filters = Object.keys(filterData)
.map((key) => {
const value = filterData[key].value;
if (
value !== "" &&
!(
Array.isArray(value) &&
(value.length === 0 || (value.length === 2 && value[0] === "" && value[1] === ""))
)
) {
return filterData[key];
}
})
.filter(Boolean);
const storedFilters = settingStore?.[user_id]?.[page_name]?.[table_name]?.filters;
const finalFilters = storedFilters || filters;
if (isTableInstanceProp(props)) {
table = props.table;
} else {
// eslint-disable-next-line react-hooks/rules-of-hooks
table = useMaterialReactTable(props);
}
const fetchUrl = useMemo(() => {
const params = new URLSearchParams();
params.set("start", `${pagination.pageIndex * pagination.pageSize}`);
params.set("size", pagination.pageSize);
params.set("filters", JSON.stringify(finalFilters ?? []));
params.set("sorting", JSON.stringify(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] ?? []));
return `${table_url}?${params}`;
}, [table_url, finalFilters, pagination, columns]);
return <DataTable_Paper table={table} />;
const onColumnVisibilityChange = (event) => {
const settingValue = event();
hideAction(user_id, page_name, table_name, settingValue, flatColumns);
};
const onSortingChange = (event) => {
const settingValue = event(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || []);
sortAction(user_id, page_name, table_name, settingValue, flatColumns);
};
const table = useMaterialReactTable({
localization: FA_DATATABLE_LOCALIZATION,
columns,
data: data,
// manualSorting: true,
renderTopToolbarCustomActions: ({ table }) => (
<>
<Toolbar columns={flatColumns} />
</>
),
initialState: {
density: "compact",
columnVisibility: settingStore?.[user_id]?.[page_name]?.[table_name]?.["hides"],
sorting: settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || [],
...initialStateProps,
},
state: {
columnVisibility: settingStore?.[user_id]?.[page_name]?.[table_name]?.["hides"],
sorting: settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || [],
pagination,
},
muiTableHeadProps: {
sx: {
borderTop: "1px solid #e1e1e1",
borderBottom: "2px solid #e1e1e1",
},
},
muiTableHeadCellProps: {
sx: {
color: "primary.main",
borderLeft: "1px solid #e1e1e1",
"&:first-of-type": {
borderLeft: "unset",
},
},
},
muiTableBodyCellProps: {
sx: {
borderLeft: "1px solid #e1e1e1",
"&:first-of-type": {
borderLeft: "unset",
},
},
},
manualFiltering: true,
manualPagination: true,
onPaginationChange: setPagination,
manualSorting: true,
onColumnVisibilityChange: onColumnVisibilityChange,
onSortingChange: onSortingChange,
...props,
});
return (
<DataTable_Paper
table={table}
columns={flatColumns}
user_id={user_id}
page_name={page_name}
table_name={table_name}
/>
);
};
export default DataTable_Main;

View File

@@ -4,11 +4,7 @@ import { useMRT_RowVirtualizer, useMRT_Rows } from "material-react-table";
import { memo, useMemo } from "react";
import DataTable_TableBodyRow, { Memo_DataTable_TableBodyRow } from "./TableBodyRow";
const DataTable_TableBody = ({
columnVirtualizer,
table,
...rest
}) => {
const DataTable_TableBody = ({ columnVirtualizer, table, ...rest }) => {
const {
getBottomRows,
getIsSomeRowsPinned,
@@ -35,12 +31,8 @@ const DataTable_TableBody = ({
...rest,
};
const tableHeadHeight =
((enableStickyHeader || isFullScreen) &&
tableHeadRef.current?.clientHeight) ||
0;
const tableFooterHeight =
(enableStickyFooter && tableFooterRef.current?.clientHeight) || 0;
const tableHeadHeight = ((enableStickyHeader || isFullScreen) && tableHeadRef.current?.clientHeight) || 0;
const tableFooterHeight = (enableStickyFooter && tableFooterRef.current?.clientHeight) || 0;
const pinnedRowIds = useMemo(() => {
if (!rowPinning.bottom?.length && !rowPinning.top?.length) return [];
@@ -63,67 +55,63 @@ const DataTable_TableBody = ({
return (
<>
{!rowPinningDisplayMode?.includes('sticky') &&
getIsSomeRowsPinned('top') && (
<TableBody
{...tableBodyProps}
sx={(theme) => ({
display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
position: 'sticky',
top: tableHeadHeight - 1,
zIndex: 1,
...(parseFromValuesOrFunc(tableBodyProps?.sx, theme)),
})}
>
{getTopRows().map((row, staticRowIndex) => {
const props = {
...commonRowProps,
row,
staticRowIndex,
};
return memoMode === 'rows' ? (
<Memo_DataTable_TableBodyRow key={row.id} {...props} />
) : (
<DataTable_TableBodyRow key={row.id} {...props} />
);
})}
</TableBody>
)}
{!rowPinningDisplayMode?.includes("sticky") && getIsSomeRowsPinned("top") && (
<TableBody
{...tableBodyProps}
sx={(theme) => ({
display: layoutMode?.startsWith("grid") ? "grid" : undefined,
position: "sticky",
top: tableHeadHeight - 1,
zIndex: 1,
...parseFromValuesOrFunc(tableBodyProps?.sx, theme),
})}
>
{getTopRows().map((row, staticRowIndex) => {
const props = {
...commonRowProps,
row,
staticRowIndex,
};
return memoMode === "rows" ? (
<Memo_DataTable_TableBodyRow key={row.id} {...props} />
) : (
<DataTable_TableBodyRow key={row.id} {...props} />
);
})}
</TableBody>
)}
<TableBody
{...tableBodyProps}
sx={(theme) => ({
display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
height: rowVirtualizer
? `${rowVirtualizer.getTotalSize()}px`
: undefined,
minHeight: !rows.length ? '100px' : undefined,
position: 'relative',
...(parseFromValuesOrFunc(tableBodyProps?.sx, theme)),
display: layoutMode?.startsWith("grid") ? "grid" : undefined,
height: rowVirtualizer ? `${rowVirtualizer.getTotalSize()}px` : undefined,
minHeight: !rows.length ? "100px" : undefined,
position: "relative",
...parseFromValuesOrFunc(tableBodyProps?.sx, theme),
})}
>
{tableBodyProps?.children ??
(!rows.length ? (
<tr
style={{
display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
display: layoutMode?.startsWith("grid") ? "grid" : undefined,
}}
>
<td
colSpan={table.getVisibleLeafColumns().length}
style={{
display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
display: layoutMode?.startsWith("grid") ? "grid" : undefined,
}}
>
{renderEmptyRowsFallback?.({ table }) ?? (
<Typography
sx={{
color: 'text.secondary',
fontStyle: 'italic',
maxWidth: `min(100vw, ${tablePaperRef.current?.clientWidth ?? 360
}px)`,
py: '2rem',
textAlign: 'center',
width: '100%',
color: "text.secondary",
fontStyle: "italic",
maxWidth: `min(100vw, ${tablePaperRef.current?.clientWidth ?? 360}px)`,
py: "2rem",
textAlign: "center",
width: "100%",
}}
>
{globalFilter || columnFilters.length
@@ -155,12 +143,10 @@ const DataTable_TableBody = ({
row,
rowVirtualizer,
staticRowIndex,
virtualRow: rowVirtualizer
? (rowOrVirtualRow)
: undefined,
virtualRow: rowVirtualizer ? rowOrVirtualRow : undefined,
};
const key = `${row.id}-${row.index}`;
return memoMode === 'rows' ? (
return memoMode === "rows" ? (
<Memo_DataTable_TableBodyRow key={key} {...props} />
) : (
<DataTable_TableBodyRow key={key} {...props} />
@@ -169,39 +155,37 @@ const DataTable_TableBody = ({
</>
))}
</TableBody>
{!rowPinningDisplayMode?.includes('sticky') &&
getIsSomeRowsPinned('bottom') && (
<TableBody
{...tableBodyProps}
sx={(theme) => ({
bottom: tableFooterHeight - 1,
display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
position: 'sticky',
zIndex: 1,
...(parseFromValuesOrFunc(tableBodyProps?.sx, theme)),
})}
>
{getBottomRows().map((row, staticRowIndex) => {
const props = {
...commonRowProps,
row,
staticRowIndex,
};
return memoMode === 'rows' ? (
<Memo_DataTable_TableBodyRow key={row.id} {...props} />
) : (
<DataTable_TableBodyRow key={row.id} {...props} />
);
})}
</TableBody>
)}
{!rowPinningDisplayMode?.includes("sticky") && getIsSomeRowsPinned("bottom") && (
<TableBody
{...tableBodyProps}
sx={(theme) => ({
bottom: tableFooterHeight - 1,
display: layoutMode?.startsWith("grid") ? "grid" : undefined,
position: "sticky",
zIndex: 1,
...parseFromValuesOrFunc(tableBodyProps?.sx, theme),
})}
>
{getBottomRows().map((row, staticRowIndex) => {
const props = {
...commonRowProps,
row,
staticRowIndex,
};
return memoMode === "rows" ? (
<Memo_DataTable_TableBodyRow key={row.id} {...props} />
) : (
<DataTable_TableBodyRow key={row.id} {...props} />
);
})}
</TableBody>
)}
</>
);
}
export default DataTable_TableBody
};
export default DataTable_TableBody;
export const Memo_DataTable_TableBody = memo(
DataTable_TableBody,
(prev, next) => prev.table.options.data === next.table.options.data,
);
(prev, next) => prev.table.options.data === next.table.options.data
);

View File

@@ -7,15 +7,7 @@ const { useState, useEffect, useMemo, memo } = require("react");
const { default: DataTable_CopyButton } = require("../buttons/CopyButton");
const { default: DataTable_TableBodyCellValue } = require("./TableBodyCellValue");
const DataTable_TableBodyCell = ({
cell,
numRows,
rowRef,
staticColumnIndex,
staticRowIndex,
table,
...rest
}) => {
const DataTable_TableBodyCell = ({ cell, numRows, rowRef, staticColumnIndex, staticRowIndex, table, ...rest }) => {
const theme = useTheme();
const {
getState,
@@ -73,9 +65,7 @@ const DataTable_TableBodyCell = ({
if ((!isLoading && !showSkeletons) || skeletonWidth !== 100) return;
const size = column.getSize();
setSkeletonWidth(
columnDefType === 'display'
? size / 2
: Math.round(Math.random() * (size - size / 3) + size / 3),
columnDefType === "display" ? size / 2 : Math.round(Math.random() * (size - size / 3) + size / 3)
);
}, [isLoading, showSkeletons]);
@@ -88,77 +78,55 @@ const DataTable_TableBodyCell = ({
const isLastColumn = column.getIsLastColumn();
const isLastRow = numRows && staticRowIndex === numRows - 1;
const isResizingColumn = columnSizingInfo.isResizingColumn === column.id;
const showResizeBorder =
isResizingColumn && columnResizeMode === 'onChange';
const showResizeBorder = isResizingColumn && columnResizeMode === "onChange";
const borderStyle = showResizeBorder
? `2px solid ${draggingBorderColor} !important`
: isDraggingColumn || isDraggingRow
? `1px dashed ${theme.palette.grey[500]} !important`
: isHoveredColumn || isHoveredRow || isResizingColumn
? `2px dashed ${draggingBorderColor} !important`
: undefined;
? `1px dashed ${theme.palette.grey[500]} !important`
: isHoveredColumn || isHoveredRow || isResizingColumn
? `2px dashed ${draggingBorderColor} !important`
: undefined;
if (showResizeBorder) {
return columnResizeDirection === 'ltr'
? { borderRight: borderStyle }
: { borderLeft: borderStyle };
return columnResizeDirection === "ltr" ? { borderRight: borderStyle } : { borderLeft: borderStyle };
}
return borderStyle
? {
borderBottom:
isDraggingRow || isHoveredRow || (isLastRow && !isResizingColumn)
? borderStyle
: undefined,
borderLeft:
isDraggingColumn ||
isHoveredColumn ||
((isDraggingRow || isHoveredRow) && isFirstColumn)
? borderStyle
: undefined,
borderRight:
isDraggingColumn ||
isHoveredColumn ||
((isDraggingRow || isHoveredRow) && isLastColumn)
? borderStyle
: undefined,
borderTop: isDraggingRow || isHoveredRow ? borderStyle : undefined,
}
borderBottom:
isDraggingRow || isHoveredRow || (isLastRow && !isResizingColumn) ? borderStyle : undefined,
borderLeft:
isDraggingColumn || isHoveredColumn || ((isDraggingRow || isHoveredRow) && isFirstColumn)
? borderStyle
: undefined,
borderRight:
isDraggingColumn || isHoveredColumn || ((isDraggingRow || isHoveredRow) && isLastColumn)
? borderStyle
: undefined,
borderTop: isDraggingRow || isHoveredRow ? borderStyle : undefined,
}
: undefined;
}, [
columnSizingInfo.isResizingColumn,
draggingColumn,
draggingRow,
hoveredColumn,
hoveredRow,
staticRowIndex,
]);
}, [columnSizingInfo.isResizingColumn, draggingColumn, draggingRow, hoveredColumn, hoveredRow, staticRowIndex]);
const isColumnPinned =
enableColumnPinning &&
columnDef.columnDefType !== 'group' &&
column.getIsPinned();
const isColumnPinned = enableColumnPinning && columnDef.columnDefType !== "group" && column.getIsPinned();
const isEditable = isCellEditable({ cell, table });
const isEditing =
isEditable &&
!['custom', 'modal'].includes(editDisplayMode) &&
(editDisplayMode === 'table' ||
editingRow?.id === row.id ||
editingCell?.id === cell.id) &&
!["custom", "modal"].includes(editDisplayMode) &&
(editDisplayMode === "table" || editingRow?.id === row.id || editingCell?.id === cell.id) &&
!row.getIsGrouped();
const isCreating =
isEditable && createDisplayMode === 'row' && creatingRow?.id === row.id;
const isCreating = isEditable && createDisplayMode === "row" && creatingRow?.id === row.id;
const showClickToCopyButton =
(parseFromValuesOrFunc(enableClickToCopy, cell) === true ||
parseFromValuesOrFunc(columnDef.enableClickToCopy, cell) === true) &&
!['context-menu', false].includes(
!["context-menu", false].includes(
// @ts-ignore
parseFromValuesOrFunc(columnDef.enableClickToCopy, cell),
parseFromValuesOrFunc(columnDef.enableClickToCopy, cell)
);
const isRightClickable = parseFromValuesOrFunc(enableCellActions, cell);
@@ -175,13 +143,11 @@ const DataTable_TableBodyCell = ({
const handleDragEnter = (e) => {
tableCellProps?.onDragEnter?.(e);
if (enableGrouping && hoveredColumn?.id === 'drop-zone') {
if (enableGrouping && hoveredColumn?.id === "drop-zone") {
setHoveredColumn(null);
}
if (enableColumnOrdering && draggingColumn) {
setHoveredColumn(
columnDef.enableColumnOrdering !== false ? column : null,
);
setHoveredColumn(columnDef.enableColumnOrdering !== false ? column : null);
}
};
@@ -210,43 +176,39 @@ const DataTable_TableBodyCell = ({
onDragEnter={handleDragEnter}
onDragOver={handleDragOver}
sx={(theme) => ({
'&:hover': {
"&:hover": {
outline:
actionCell?.id === cell.id ||
(editDisplayMode === 'cell' && isEditable) ||
(editDisplayMode === 'table' && (isCreating || isEditing))
(editDisplayMode === "cell" && isEditable) ||
(editDisplayMode === "table" && (isCreating || isEditing))
? `1px solid ${theme.palette.grey[500]}`
: undefined,
textOverflow: 'clip',
textOverflow: "clip",
},
alignItems: layoutMode?.startsWith('grid') ? 'center' : undefined,
alignItems: layoutMode?.startsWith("grid") ? "center" : undefined,
cursor: isRightClickable
? 'context-menu'
: isEditable && editDisplayMode === 'cell'
? 'pointer'
: 'inherit',
outline:
actionCell?.id === cell.id
? `1px solid ${theme.palette.grey[500]}`
: undefined,
outlineOffset: '-1px',
overflow: 'hidden',
? "context-menu"
: isEditable && editDisplayMode === "cell"
? "pointer"
: "inherit",
outline: actionCell?.id === cell.id ? `1px solid ${theme.palette.grey[500]}` : undefined,
outlineOffset: "-1px",
overflow: "hidden",
p:
density === 'compact'
? columnDefType === 'display'
? '0 0.5rem'
: '0.5rem'
: density === 'comfortable'
? columnDefType === 'display'
? '0.5rem 0.75rem'
: '1rem'
: columnDefType === 'display'
? '1rem 1.25rem'
: '1.5rem',
density === "compact"
? columnDefType === "display"
? "0 0.5rem"
: "0.5rem"
: density === "comfortable"
? columnDefType === "display"
? "0.5rem 0.75rem"
: "1rem"
: columnDefType === "display"
? "1rem 1.25rem"
: "1.5rem",
textOverflow: columnDefType !== 'display' ? 'ellipsis' : undefined,
whiteSpace:
row.getIsPinned() || density === 'compact' ? 'nowrap' : 'normal',
textOverflow: columnDefType !== "display" ? "ellipsis" : undefined,
whiteSpace: row.getIsPinned() || density === "compact" ? "nowrap" : "normal",
...getCommonMRTCellStyles({
column,
table,
@@ -261,17 +223,10 @@ const DataTable_TableBodyCell = ({
{cell.getIsPlaceholder() ? (
columnDef.PlaceholderCell?.({ cell, column, row, table }) ?? null
) : showSkeletons !== false && (isLoading || showSkeletons) ? (
<Skeleton
animation="wave"
height={20}
width={skeletonWidth}
{...skeletonProps}
/>
) : columnDefType === 'display' &&
(['mrt-row-expand', 'mrt-row-numbers', 'mrt-row-select'].includes(
column.id,
) ||
!row.getIsGrouped()) ? (
<Skeleton animation="wave" height={20} width={skeletonWidth} {...skeletonProps} />
) : columnDefType === "display" &&
(["mrt-row-expand", "mrt-row-numbers", "mrt-row-select"].includes(column.id) ||
!row.getIsGrouped()) ? (
columnDef.Cell?.({
cell,
column,
@@ -289,18 +244,12 @@ const DataTable_TableBodyCell = ({
) : (
<DataTable_TableBodyCellValue {...cellValueProps} />
)}
{cell.getIsGrouped() && !columnDef.GroupedCell && (
<> ({row.subRows?.length})</>
)}
{cell.getIsGrouped() && !columnDef.GroupedCell && <> ({row.subRows?.length})</>}
</>
)}
</TableCell>
);
}
};
export default DataTable_TableBodyCell;
export const Memo_DataTable_TableBodyCell = memo(
DataTable_TableBodyCell,
(prev, next) => next.cell === prev.cell,
);
export const Memo_DataTable_TableBodyCell = memo(DataTable_TableBodyCell, (prev, next) => next.cell === prev.cell);

View File

@@ -2,111 +2,102 @@ import { Box } from "@mui/material";
const allowedTypes = ["string", "number"];
const DataTable_TableBodyCellValue = ({
cell,
rowRef,
staticColumnIndex,
staticRowIndex,
table,
}) => {
const {
getState,
options: {
enableFilterMatchHighlighting,
mrtTheme: { matchHighlightColor },
},
} = table;
const { column, row } = cell;
const { columnDef } = column;
const { globalFilter, globalFilterFn } = getState();
const filterValue = column.getFilterValue();
const DataTable_TableBodyCellValue = ({ cell, rowRef, staticColumnIndex, staticRowIndex, table }) => {
const {
getState,
options: {
enableFilterMatchHighlighting,
mrtTheme: { matchHighlightColor },
},
} = table;
const { column, row } = cell;
const { columnDef } = column;
const { globalFilter, globalFilterFn } = getState();
const filterValue = column.getFilterValue();
let renderedCellValue =
cell.getIsAggregated() && columnDef.AggregatedCell
? columnDef.AggregatedCell({
cell,
column,
row,
table,
})
: row.getIsGrouped() && !cell.getIsGrouped()
? null
: cell.getIsGrouped() && columnDef.GroupedCell
? columnDef.GroupedCell({
let renderedCellValue =
cell.getIsAggregated() && columnDef.AggregatedCell
? columnDef.AggregatedCell({
cell,
column,
row,
table,
})
: row.getIsGrouped() && !cell.getIsGrouped()
? null
: cell.getIsGrouped() && columnDef.GroupedCell
? columnDef.GroupedCell({
cell,
column,
row,
table,
})
: undefined;
const isGroupedValue = renderedCellValue !== undefined;
if (!isGroupedValue) {
renderedCellValue = cell.renderValue();
}
if (
enableFilterMatchHighlighting &&
columnDef.enableFilterMatchHighlighting !== false &&
String(renderedCellValue) &&
allowedTypes.includes(typeof renderedCellValue) &&
((filterValue &&
allowedTypes.includes(typeof filterValue) &&
["autocomplete", "text"].includes(columnDef.filterVariant)) ||
(globalFilter && allowedTypes.includes(typeof globalFilter) && column.getCanGlobalFilter()))
) {
const chunks = highlightWords?.({
matchExactly: (filterValue ? columnDef._filterFn : globalFilterFn) !== "fuzzy",
query: (filterValue ?? globalFilter ?? "").toString(),
text: renderedCellValue?.toString(),
});
if (chunks?.length > 1 || chunks?.[0]?.match) {
renderedCellValue = (
<span aria-label={renderedCellValue} role="note">
{chunks?.map(({ key, match, text }) => (
<Box
aria-hidden="true"
component="span"
key={key}
sx={
match
? {
backgroundColor: matchHighlightColor,
borderRadius: "2px",
color: (theme) =>
theme.palette.mode === "dark"
? theme.palette.common.white
: theme.palette.common.black,
padding: "2px 1px",
}
: undefined
}
>
{text}
</Box>
)) ?? renderedCellValue}
</span>
);
}
}
if (columnDef.Cell && !isGroupedValue) {
renderedCellValue = columnDef.Cell({
cell,
column,
renderedCellValue,
row,
rowRef,
staticColumnIndex,
staticRowIndex,
table,
})
: undefined;
const isGroupedValue = renderedCellValue !== undefined;
if (!isGroupedValue) {
renderedCellValue = cell.renderValue();
}
if (
enableFilterMatchHighlighting &&
columnDef.enableFilterMatchHighlighting !== false &&
String(renderedCellValue) &&
allowedTypes.includes(typeof renderedCellValue) &&
((filterValue &&
allowedTypes.includes(typeof filterValue) &&
["autocomplete", "text"].includes(columnDef.filterVariant)) ||
(globalFilter &&
allowedTypes.includes(typeof globalFilter) &&
column.getCanGlobalFilter()))
) {
const chunks = highlightWords?.({
matchExactly:
(filterValue ? columnDef._filterFn : globalFilterFn) !== "fuzzy",
query: (filterValue ?? globalFilter ?? "").toString(),
text: renderedCellValue?.toString(),
});
if (chunks?.length > 1 || chunks?.[0]?.match) {
renderedCellValue = (
<span aria-label={renderedCellValue} role="note">
{chunks?.map(({ key, match, text }) => (
<Box
aria-hidden="true"
component="span"
key={key}
sx={
match
? {
backgroundColor: matchHighlightColor,
borderRadius: "2px",
color: (theme) =>
theme.palette.mode === "dark"
? theme.palette.common.white
: theme.palette.common.black,
padding: "2px 1px",
}
: undefined
}
>
{text}
</Box>
)) ?? renderedCellValue}
</span>
);
});
}
}
if (columnDef.Cell && !isGroupedValue) {
renderedCellValue = columnDef.Cell({
cell,
column,
renderedCellValue,
row,
rowRef,
staticColumnIndex,
staticRowIndex,
table,
});
}
return renderedCellValue;
return renderedCellValue;
};
export default DataTable_TableBodyCellValue;

View File

@@ -28,11 +28,7 @@ const DataTable_TableBodyRow = ({
enableStickyHeader,
layoutMode,
memoMode,
mrtTheme: {
baseBackgroundColor,
pinnedRowBackgroundColor,
selectedRowBackgroundColor,
},
mrtTheme: { baseBackgroundColor, pinnedRowBackgroundColor, selectedRowBackgroundColor },
muiTableBodyRowProps,
renderDetailPanel,
rowPinningDisplayMode,
@@ -40,21 +36,12 @@ const DataTable_TableBodyRow = ({
refs: { tableFooterRef, tableHeadRef },
setHoveredRow,
} = table;
const {
density,
draggingColumn,
draggingRow,
editingCell,
editingRow,
hoveredRow,
isFullScreen,
rowPinning,
} = getState();
const { density, draggingColumn, draggingRow, editingCell, editingRow, hoveredRow, isFullScreen, rowPinning } =
getState();
const visibleCells = row.getVisibleCells();
const { virtualColumns, virtualPaddingLeft, virtualPaddingRight } =
columnVirtualizer ?? {};
const { virtualColumns, virtualPaddingLeft, virtualPaddingRight } = columnVirtualizer ?? {};
const isRowSelected = getIsRowSelected({ row, table });
const isRowPinned = enableRowPinning && row.getIsPinned();
@@ -71,30 +58,17 @@ const DataTable_TableBodyRow = ({
};
const [bottomPinnedIndex, topPinnedIndex] = useMemo(() => {
if (
!enableRowPinning ||
!rowPinningDisplayMode?.includes('sticky') ||
!pinnedRowIds ||
!row.getIsPinned()
)
if (!enableRowPinning || !rowPinningDisplayMode?.includes("sticky") || !pinnedRowIds || !row.getIsPinned())
return [];
return [
[...pinnedRowIds].reverse().indexOf(row.id),
pinnedRowIds.indexOf(row.id),
];
return [[...pinnedRowIds].reverse().indexOf(row.id), pinnedRowIds.indexOf(row.id)];
}, [pinnedRowIds, rowPinning]);
const tableHeadHeight =
((enableStickyHeader || isFullScreen) &&
tableHeadRef.current?.clientHeight) ||
0;
const tableFooterHeight =
(enableStickyFooter && tableFooterRef.current?.clientHeight) || 0;
const tableHeadHeight = ((enableStickyHeader || isFullScreen) && tableHeadRef.current?.clientHeight) || 0;
const tableFooterHeight = (enableStickyFooter && tableFooterRef.current?.clientHeight) || 0;
const sx = parseFromValuesOrFunc(tableRowProps?.sx, theme);
const defaultRowHeight =
density === 'compact' ? 37 : density === 'comfortable' ? 53 : 69;
const defaultRowHeight = density === "compact" ? 37 : density === "comfortable" ? 53 : 69;
const customRowHeight =
// @ts-ignore
@@ -117,16 +91,16 @@ const DataTable_TableBodyRow = ({
const cellHighlightColor = isRowSelected
? selectedRowBackgroundColor
: isRowPinned
? pinnedRowBackgroundColor
: undefined;
? pinnedRowBackgroundColor
: undefined;
const cellHighlightColorHover =
tableRowProps?.hover !== false
? isRowSelected
? cellHighlightColor
: theme.palette.mode === 'dark'
? `${lighten(baseBackgroundColor, 0.3)}`
: `${darken(baseBackgroundColor, 0.3)}`
: theme.palette.mode === "dark"
? `${lighten(baseBackgroundColor, 0.3)}`
: `${darken(baseBackgroundColor, 0.3)}`
: undefined;
return (
@@ -146,91 +120,81 @@ const DataTable_TableBodyRow = ({
selected={isRowSelected}
{...tableRowProps}
style={{
transform: virtualRow
? `translateY(${virtualRow.start}px)`
: undefined,
transform: virtualRow ? `translateY(${virtualRow.start}px)` : undefined,
...tableRowProps?.style,
}}
sx={(theme) => ({
'&:hover td:after': cellHighlightColorHover
"&:hover td:after": cellHighlightColorHover
? {
backgroundColor: alpha(cellHighlightColorHover, 0.3),
...commonCellBeforeAfterStyles,
}
backgroundColor: alpha(cellHighlightColorHover, 0.3),
...commonCellBeforeAfterStyles,
}
: undefined,
backgroundColor: `${baseBackgroundColor} !important`,
bottom:
!virtualRow && bottomPinnedIndex !== undefined && isRowPinned
? `${bottomPinnedIndex * rowHeight +
(enableStickyFooter ? tableFooterHeight - 1 : 0)
}px`
? `${bottomPinnedIndex * rowHeight + (enableStickyFooter ? tableFooterHeight - 1 : 0)}px`
: undefined,
boxSizing: 'border-box',
display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
boxSizing: "border-box",
display: layoutMode?.startsWith("grid") ? "flex" : undefined,
opacity: isRowPinned ? 0.97 : isDraggingRow || isHoveredRow ? 0.5 : 1,
position: virtualRow
? 'absolute'
: rowPinningDisplayMode?.includes('sticky') && isRowPinned
? 'sticky'
: 'relative',
? "absolute"
: rowPinningDisplayMode?.includes("sticky") && isRowPinned
? "sticky"
: "relative",
td: {
...getCommonPinnedCellStyles({ table, theme }),
},
'td:after': cellHighlightColor
"td:after": cellHighlightColor
? {
backgroundColor: cellHighlightColor,
...commonCellBeforeAfterStyles,
}
backgroundColor: cellHighlightColor,
...commonCellBeforeAfterStyles,
}
: undefined,
top: virtualRow
? 0
: topPinnedIndex !== undefined && isRowPinned
? `${topPinnedIndex * rowHeight +
(enableStickyHeader || isFullScreen ? tableHeadHeight - 1 : 0)
? `${
topPinnedIndex * rowHeight +
(enableStickyHeader || isFullScreen ? tableHeadHeight - 1 : 0)
}px`
: undefined,
transition: virtualRow ? 'none' : 'all 150ms ease-in-out',
width: '100%',
zIndex:
rowPinningDisplayMode?.includes('sticky') && isRowPinned ? 2 : 0,
...(sx),
: undefined,
transition: virtualRow ? "none" : "all 150ms ease-in-out",
width: "100%",
zIndex: rowPinningDisplayMode?.includes("sticky") && isRowPinned ? 2 : 0,
...sx,
})}
>
{virtualPaddingLeft ? (
<td style={{ display: 'flex', width: virtualPaddingLeft }} />
) : null}
{(virtualColumns ?? visibleCells).map(
(cellOrVirtualCell, staticColumnIndex) => {
let cell = cellOrVirtualCell;
if (columnVirtualizer) {
staticColumnIndex = (cellOrVirtualCell).index;
cell = visibleCells[staticColumnIndex];
}
const props = {
cell,
numRows,
rowRef,
staticColumnIndex,
staticRowIndex,
table,
};
return cell ? (
memoMode === 'cells' &&
cell.column.columnDef.columnDefType === 'data' &&
!draggingColumn &&
!draggingRow &&
editingCell?.id !== cell.id &&
editingRow?.id !== row.id ? (
<Memo_DataTable_TableBodyCell key={cell.id} {...props} />
) : (
<DataTable_TableBodyCell key={cell.id} {...props} />
)
) : null;
},
)}
{virtualPaddingRight ? (
<td style={{ display: 'flex', width: virtualPaddingRight }} />
) : null}
{virtualPaddingLeft ? <td style={{ display: "flex", width: virtualPaddingLeft }} /> : null}
{(virtualColumns ?? visibleCells).map((cellOrVirtualCell, staticColumnIndex) => {
let cell = cellOrVirtualCell;
if (columnVirtualizer) {
staticColumnIndex = cellOrVirtualCell.index;
cell = visibleCells[staticColumnIndex];
}
const props = {
cell,
numRows,
rowRef,
staticColumnIndex,
staticRowIndex,
table,
};
return cell ? (
memoMode === "cells" &&
cell.column.columnDef.columnDefType === "data" &&
!draggingColumn &&
!draggingRow &&
editingCell?.id !== cell.id &&
editingRow?.id !== row.id ? (
<Memo_DataTable_TableBodyCell key={cell.id} {...props} />
) : (
<DataTable_TableBodyCell key={cell.id} {...props} />
)
) : null;
})}
{virtualPaddingRight ? <td style={{ display: "flex", width: virtualPaddingRight }} /> : null}
</TableRow>
{renderDetailPanel && !row.getIsGrouped() && (
<DataTable_TableDetailPanel
@@ -244,11 +208,10 @@ const DataTable_TableBodyRow = ({
)}
</>
);
}
export default DataTable_TableBodyRow
};
export default DataTable_TableBodyRow;
export const Memo_DataTable_TableBodyRow = memo(
DataTable_TableBodyRow,
(prev, next) =>
prev.row === next.row && prev.staticRowIndex === next.staticRowIndex,
);
(prev, next) => prev.row === next.row && prev.staticRowIndex === next.staticRowIndex
);

View File

@@ -23,7 +23,6 @@ const DataTable_TableDetailPanel = ({
} = table;
const { isLoading } = getState();
const tableRowProps = parseFromValuesOrFunc(muiTableBodyRowProps, {
isDetailPanel: true,
row,
@@ -52,16 +51,12 @@ const DataTable_TableDetailPanel = ({
}}
{...tableRowProps}
sx={(theme) => ({
display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
position: virtualRow ? 'absolute' : undefined,
top: virtualRow
? `${parentRowRef.current?.getBoundingClientRect()?.height}px`
: undefined,
transform: virtualRow
? `translateY(${virtualRow?.start}px)`
: undefined,
width: '100%',
...(parseFromValuesOrFunc(tableRowProps?.sx, theme)),
display: layoutMode?.startsWith("grid") ? "flex" : undefined,
position: virtualRow ? "absolute" : undefined,
top: virtualRow ? `${parentRowRef.current?.getBoundingClientRect()?.height}px` : undefined,
transform: virtualRow ? `translateY(${virtualRow?.start}px)` : undefined,
width: "100%",
...parseFromValuesOrFunc(tableRowProps?.sx, theme),
})}
>
<TableCell
@@ -70,12 +65,12 @@ const DataTable_TableDetailPanel = ({
{...tableCellProps}
sx={(theme) => ({
backgroundColor: virtualRow ? baseBackgroundColor : undefined,
borderBottom: !row.getIsExpanded() ? 'none' : undefined,
display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
py: !!DetailPanel && row.getIsExpanded() ? '1rem' : 0,
transition: !virtualRow ? 'all 150ms ease-in-out' : undefined,
borderBottom: !row.getIsExpanded() ? "none" : undefined,
display: layoutMode?.startsWith("grid") ? "flex" : undefined,
py: !!DetailPanel && row.getIsExpanded() ? "1rem" : 0,
transition: !virtualRow ? "all 150ms ease-in-out" : undefined,
width: `100%`,
...(parseFromValuesOrFunc(tableCellProps?.sx, theme)),
...parseFromValuesOrFunc(tableCellProps?.sx, theme),
})}
>
{virtualRow ? (
@@ -88,6 +83,5 @@ const DataTable_TableDetailPanel = ({
</TableCell>
</TableRow>
);
}
export default DataTable_TableDetailPanel
};
export default DataTable_TableDetailPanel;

View File

@@ -2,11 +2,7 @@ import { getCommonTooltipProps, parseFromValuesOrFunc } from "@/core/utils/utils
import { Button, Tooltip } from "@mui/material";
import { useState } from "react";
const DataTable_CopyButton = ({
cell,
table,
...rest
}) => {
const DataTable_CopyButton = ({ cell, table, ...rest }) => {
const {
options: { localization, muiCopyButtonProps },
} = table;
@@ -40,11 +36,8 @@ const DataTable_CopyButton = ({
return (
<Tooltip
{...getCommonTooltipProps('top')}
title={
buttonProps?.title ??
(copied ? localization.copiedToClipboard : localization.clickToCopy)
}
{...getCommonTooltipProps("top")}
title={buttonProps?.title ?? (copied ? localization.copiedToClipboard : localization.clickToCopy)}
>
<Button
onClick={(e) => handleCopy(e, cell.getValue())}
@@ -53,23 +46,23 @@ const DataTable_CopyButton = ({
variant="text"
{...buttonProps}
sx={(theme) => ({
backgroundColor: 'transparent',
border: 'none',
color: 'inherit',
cursor: 'copy',
fontFamily: 'inherit',
fontSize: 'inherit',
letterSpacing: 'inherit',
m: '-0.25rem',
minWidth: 'unset',
backgroundColor: "transparent",
border: "none",
color: "inherit",
cursor: "copy",
fontFamily: "inherit",
fontSize: "inherit",
letterSpacing: "inherit",
m: "-0.25rem",
minWidth: "unset",
py: 0,
textAlign: 'inherit',
textTransform: 'inherit',
...(parseFromValuesOrFunc(buttonProps?.sx, theme)),
textAlign: "inherit",
textTransform: "inherit",
...parseFromValuesOrFunc(buttonProps?.sx, theme),
})}
title={undefined}
/>
</Tooltip>
);
}
export default DataTable_CopyButton
};
export default DataTable_CopyButton;

View File

@@ -1,11 +1,7 @@
import { getCommonTooltipProps, parseFromValuesOrFunc } from "@/core/utils/utils";
import { IconButton, Tooltip } from "@mui/material";
const DataTable_GrabHandleButton = ({
location,
table,
...rest
}) => {
const DataTable_GrabHandleButton = ({ location, table, ...rest }) => {
const {
options: {
icons: { DragHandleIcon },
@@ -14,10 +10,7 @@ const DataTable_GrabHandleButton = ({
} = table;
return (
<Tooltip
{...getCommonTooltipProps('top')}
title={rest?.title ?? localization.move}
>
<Tooltip {...getCommonTooltipProps("top")} title={rest?.title ?? localization.move}>
<IconButton
aria-label={rest.title ?? localization.move}
disableRipple
@@ -29,19 +22,19 @@ const DataTable_GrabHandleButton = ({
rest?.onClick?.(e);
}}
sx={(theme) => ({
'&:active': {
cursor: 'grabbing',
"&:active": {
cursor: "grabbing",
},
'&:hover': {
backgroundColor: 'transparent',
"&:hover": {
backgroundColor: "transparent",
opacity: 1,
},
cursor: 'grab',
m: '0 -0.1rem',
opacity: location === 'row' ? 1 : 0.5,
p: '2px',
transition: 'all 150ms ease-in-out',
...(parseFromValuesOrFunc(rest?.sx, theme)),
cursor: "grab",
m: "0 -0.1rem",
opacity: location === "row" ? 1 : 0.5,
p: "2px",
transition: "all 150ms ease-in-out",
...parseFromValuesOrFunc(rest?.sx, theme),
})}
title={undefined}
>
@@ -49,6 +42,5 @@ const DataTable_GrabHandleButton = ({
</IconButton>
</Tooltip>
);
}
export default DataTable_GrabHandleButton
};
export default DataTable_GrabHandleButton;

View File

@@ -19,23 +19,12 @@ const DataTable_ShowHideColumnsButton = ({ table, ...rest }) => {
return (
<>
<Tooltip title={rest?.title ?? localization.showHideColumns}>
<IconButton
aria-label={localization.showHideColumns}
onClick={handleClick}
{...rest}
title={undefined}
>
<IconButton aria-label={localization.showHideColumns} onClick={handleClick} {...rest} title={undefined}>
<ViewColumnIcon />
</IconButton>
</Tooltip>
{anchorEl && (
<DataTable_ShowHideColumnsMenu
anchorEl={anchorEl}
setAnchorEl={setAnchorEl}
table={table}
/>
)}
{anchorEl && <DataTable_ShowHideColumnsMenu anchorEl={anchorEl} setAnchorEl={setAnchorEl} table={table} />}
</>
);
}
export default DataTable_ShowHideColumnsButton
};
export default DataTable_ShowHideColumnsButton;

View File

@@ -1,4 +1,4 @@
const DataTable_ToggleFiltersButton = ({ table, ...rest }) => {
return <></>
}
export default DataTable_ToggleFiltersButton
return <></>;
};
export default DataTable_ToggleFiltersButton;

View File

@@ -0,0 +1,224 @@
"use client";
import { Box, Button, Drawer, styled } from "@mui/material";
import { Form, Formik } from "formik";
import useTableSetting from "@/lib/hooks/useTableSetting";
import { useEffect, useRef, useState } from "react";
import FilterHeader from "@/core/components/DataTable/filter/FilterHeader";
import * as Yup from "yup";
import FilterBodyField from "@/core/components/DataTable/filter/FilterBodyField";
import { toast } from "react-toastify";
import AskForKeep from "@/core/components/NotificationDesign/AskForKeep";
import useDataTable from "@/lib/hooks/useDataTable";
const ScrollBox = styled(Box)({
flexGrow: 1,
overflowY: "scroll",
maxWidth: "450px",
"::-webkit-scrollbar": {
width: "5px",
},
"::-webkit-scrollbar-track": {
boxShadow: "inset 0 0 5px #fff",
borderRadius: "5px",
},
"::-webkit-scrollbar-thumb": {
background: "#155175",
borderRadius: "0px",
},
});
function FilterBody({ columns, drawerState, setDrawerState, user_id, page_name, table_name }) {
const { settingStore, filterAction } = useTableSetting();
const [initialValues, setInitialValues] = useState({});
const [validationSchema, setValidationSchema] = useState({});
const { filterData, setFilterData } = useDataTable();
const filterDataRef = useRef(filterData);
useEffect(() => {
filterDataRef.current = filterData;
}, [filterData]);
useEffect(() => {
const values = columns.reduce((acc, column) => {
if (!column.enableColumnFilter) return acc;
const filter = settingStore?.[user_id]?.[page_name]?.[table_name]?.["filters"]?.find(
(filter) => filter.id === column.id
);
if (column.datatype === "array") {
acc[column.id] = {
id: column.id,
value: filter?.value || [],
filterFn: filter?.fn || column._filterFn,
datatype: column.datatype,
};
} else if (column._filterFn === "between") {
acc[column.id] = {
id: column.id,
value: filter?.value || ["", ""],
filterFn: filter?.fn || column._filterFn,
datatype: column.datatype,
};
} else {
acc[column.id] = {
id: column.id,
value: filter?.value || "",
filterFn: filter?.fn || column._filterFn,
datatype: column.datatype,
};
}
return acc;
}, {});
setInitialValues(Object.keys(filterData).length !== 0 ? filterData : values);
}, [columns, settingStore, user_id, page_name, table_name, filterData]);
useEffect(() => {
const generatedSchema = Yup.object().shape(
Object.keys(initialValues).reduce((acc, key) => {
const initialValue = initialValues[key];
if (initialValue.filterFn === "between") {
acc[key] = Yup.object().shape({
value: Yup.array()
.of(Yup.string().nullable())
.test({
test(value, ctx) {
const [start, end] = value || ["", ""];
if (
initialValue.datatype === "numeric" &&
parseInt(end, 10) <= parseInt(start, 10)
) {
return ctx.createError({
message: `مقدار وارده باید بیشتر از (${start}) باشد`,
});
} else if ((start && !end) || (!start && end)) {
return ctx.createError({
message: "این بخش را تکمیل نمایید",
});
}
return true;
},
}),
});
}
return acc;
}, {})
);
setValidationSchema(generatedSchema);
}, [initialValues]);
const handleSubmit = (values, { setSubmitting }) => {
toast.dismiss({ containerId: "filtering" });
setDrawerState(false);
setFilterData(values);
toast(<AskForKeep onSaveFilter={onSaveFilter} />, {
containerId: "filtering",
className: "filter-toast",
position: "bottom-left",
draggable: true,
autoClose: false,
});
setSubmitting(false);
};
const onSaveFilter = () => {
const currentFilterData = filterDataRef.current;
const arrayOfObjects = Object.keys(currentFilterData)
.map((key) => {
const value = currentFilterData[key].value;
if (
value !== "" &&
!(
Array.isArray(value) &&
(value.length === 0 || (value.length === 2 && value[0] === "" && value[1] === ""))
)
) {
return currentFilterData[key];
}
})
.filter(Boolean);
filterAction(user_id, page_name, table_name, arrayOfObjects, columns);
toast.dismiss({ containerId: "filtering" });
};
return (
<Drawer
open={drawerState}
onClose={() => setDrawerState(false)}
sx={{
overflowY: "hidden",
display: "flex",
flexDirection: "column",
height: "100%",
}}
>
<FilterHeader setDrawerState={setDrawerState} />
{Object.keys(initialValues).length > 0 && (
<ScrollBox>
<Formik initialValues={initialValues} onSubmit={handleSubmit} validationSchema={validationSchema}>
{({
values,
handleChange,
handleBlur,
setFieldValue,
errors,
touched,
isSubmitting,
isValid,
dirty,
resetForm,
}) => (
<Form>
<Box sx={{ px: 1, py: 2 }}>
{columns.map(
(column) =>
column.enableColumnFilter && (
<FilterBodyField
key={column.id}
column={column}
filterParameters={values[column.id]}
handleChange={handleChange}
handleBlur={handleBlur}
setFieldValue={setFieldValue}
resetForm={resetForm}
errors={errors}
touched={touched}
/>
)
)}
</Box>
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
pb: 2,
}}
>
<Button
type="submit"
variant="contained"
size="large"
disabled={isSubmitting || !isValid || !dirty}
sx={{
px: 8,
boxShadow:
"rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
backgroundColor: "#155175",
":hover": {
backgroundColor: "#155175",
},
}}
>
اعمال فیلتر
</Button>
</Box>
</Form>
)}
</Formik>
</ScrollBox>
)}
</Drawer>
);
}
export default FilterBody;

View File

@@ -0,0 +1,113 @@
import { useState } from "react";
import FilterOptionList from "@/core/components/DataTable/filter/FilterOptionList";
import CustomSelect from "@/core/components/DataTable/filter/fieldsType/CustomSelect";
import CustomTextField from "@/core/components/DataTable/filter/fieldsType/CustomTextField";
import CustomTextFieldRange from "@/core/components/DataTable/filter/fieldsType/CustomTextFieldRange";
import CustomDatePickerRange from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePickerRange";
import CustomDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePicker";
import CustomSelectMultiple from "@/core/components/DataTable/filter/fieldsType/CustomSelectMultiple";
const columnFilterModeOptionFa = {
equals: "برابر",
notEquals: "نابرابر",
contains: "شامل",
lessThan: "کوچکتر",
greaterThan: "بزرگتر",
fuzzy: "فازی",
between: "مابین",
};
function FilterBodyField({
column,
filterParameters,
handleChange,
handleBlur,
setFieldValue,
errors,
touched,
resetForm,
}) {
const [anchorEl, setAnchorEl] = useState(null);
const defaultFilterTranslation = columnFilterModeOptionFa[filterParameters.filterFn] || filterParameters.filterFn;
const handleOpenFilterBox = (event) => {
setAnchorEl(event.currentTarget);
};
return (
<>
{filterParameters.datatype === "numeric" && filterParameters.filterFn === "between" ? (
<CustomTextFieldRange
column={column}
filterParameters={filterParameters}
defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox}
handleBlur={handleBlur}
handleChange={handleChange}
errors={errors}
touched={touched}
/>
) : filterParameters.datatype === "numeric" &&
filterParameters.filterFn === "equals" &&
Array.isArray(column.columnSelectOption) ? (
<CustomSelect
column={column}
filterParameters={filterParameters}
setFieldValue={setFieldValue}
defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox}
/>
) : filterParameters.datatype === "date" && filterParameters.filterFn === "equals" ? (
<CustomDatePicker
column={column}
filterParameters={filterParameters}
setFieldValue={setFieldValue}
defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox}
/>
) : filterParameters.datatype === "date" && filterParameters.filterFn === "between" ? (
<CustomDatePickerRange
column={column}
filterParameters={filterParameters}
setFieldValue={setFieldValue}
defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox}
errors={errors}
touched={touched}
/>
) : filterParameters.datatype === "array" && filterParameters.filterFn === "equals" ? (
<CustomSelectMultiple
column={column}
filterParameters={filterParameters}
setFieldValue={setFieldValue}
defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox}
/>
) : (
<CustomTextField
column={column}
filterParameters={filterParameters}
defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox}
handleBlur={handleBlur}
handleChange={(e) => setFieldValue(`${column.id}.value`, e.target.value)}
/>
)}
{Array.isArray(column.columnFilterModeOptions) && (
<FilterOptionList
column={column}
anchorEl={anchorEl}
setAnchorEl={setAnchorEl}
filterParameters={filterParameters}
filterType={filterParameters.filterFn}
setFieldValue={setFieldValue}
resetForm={resetForm}
filterOption={column.columnFilterModeOptions}
columnFilterModeOptionFa={columnFilterModeOptionFa}
/>
)}
</>
);
}
export default FilterBodyField;

View File

@@ -0,0 +1,20 @@
"use client";
import FilterAltIcon from "@mui/icons-material/FilterAlt";
import { IconButton, Tooltip } from "@mui/material";
function FilterButton({ drawerState, setDrawerState }) {
return (
<Tooltip title="فیلتر">
<IconButton
onClick={() => {
setDrawerState(!drawerState);
}}
aria-label="filter table"
>
<FilterAltIcon />
</IconButton>
</Tooltip>
);
}
export default FilterButton;

View File

@@ -0,0 +1,34 @@
"use client";
import { Box, IconButton, Typography } from "@mui/material";
import CancelIcon from "@mui/icons-material/Cancel";
import FilterAltIcon from "@mui/icons-material/FilterAlt";
function FilterHeader({ setDrawerState }) {
return (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
px: 2,
py: 1,
backgroundColor: "#155175",
boxShadow: "rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px",
maxWidth: "450px",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<FilterAltIcon sx={{ color: "#fff" }} />
<Typography variant="h6" sx={{ color: "#fff" }}>
فیلتر
</Typography>
</Box>
<IconButton sx={{ color: "#fff" }} onClick={() => setDrawerState(false)}>
<CancelIcon sx={{ fontSize: "1em" }} />
</IconButton>
</Box>
);
}
export default FilterHeader;

View File

@@ -0,0 +1,49 @@
"use client";
import { ListItem, Menu } from "@mui/material";
import { useState } from "react";
function FilterOptionList({
column,
filterType,
filterOption,
anchorEl,
columnFilterModeOptionFa,
setAnchorEl,
setFieldValue,
}) {
const [selectedFilter, setSelectedFilter] = useState(filterType);
const handleChangeItem = (event, index) => {
setFieldValue(`${column.id}.value`, filterOption[index] === "between" ? ["", ""] : "");
setFieldValue(`${column.id}.filterFn`, filterOption[index]);
setSelectedFilter(filterOption[index]);
setAnchorEl(null);
};
const handleCloseFilterBox = () => {
setAnchorEl(null);
};
return (
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleCloseFilterBox}>
{filterOption.map((option, index) => (
<ListItem
key={index}
onClick={(event) => handleChangeItem(event, index)}
selected={option === selectedFilter}
sx={{
cursor: "pointer",
width: "100px",
display: "flex",
justifyContent: "center",
}}
>
{columnFilterModeOptionFa[option]}
</ListItem>
))}
</Menu>
);
}
export default FilterOptionList;

View File

@@ -0,0 +1,21 @@
import React from "react";
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
import { Typography } from "@mui/material";
function CustomDatePicker({ column, filterParameters, defaultFilterTranslation, setFieldValue }) {
return (
<MuiDatePicker
name={`${column.id}.value`}
value={filterParameters.value}
setFieldValue={setFieldValue}
placeholder={column.header}
helperText={
<Typography variant="button" sx={{ color: "#155175" }}>
نوع فیلتر: {defaultFilterTranslation} (تاریخ)
</Typography>
}
/>
);
}
export default CustomDatePicker;

View File

@@ -0,0 +1,35 @@
"use client";
import React from "react";
import { Box, Typography } from "@mui/material";
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
function CustomDatePickerRange({ column, filterParameters, defaultFilterTranslation, setFieldValue, errors, touched }) {
return (
<Box sx={{ display: "flex", gap: 1 }}>
<MuiDatePicker
name={`${column.id}.value[0]`}
value={filterParameters.value[0]}
setFieldValue={setFieldValue}
maxDate={filterParameters.value[1]}
placeholder={`از تاریخ`}
helperText={
<Typography variant="button" sx={{ color: "#155175" }}>
نوع فیلتر: {defaultFilterTranslation} (تاریخ)
</Typography>
}
/>
<MuiDatePicker
name={`${column.id}.value[1]`}
value={filterParameters.value[1]}
setFieldValue={setFieldValue}
minDate={filterParameters.value[0]}
placeholder={`تا تاریخ`}
helperText={errors?.[`${column.id}`]?.value ? errors?.[`${column.id}`]?.value : null}
error={Boolean(errors?.[`${column.id}`]?.value)}
/>
</Box>
);
}
export default CustomDatePickerRange;

View File

@@ -1,38 +1,36 @@
import { LocalizationProvider, MobileDateTimePicker } from "@mui/x-date-pickers";
import React from "react";
import { LocalizationProvider, MobileDatePicker } from "@mui/x-date-pickers";
import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
import moment from "jalali-moment";
import { faIR } from "@mui/x-date-pickers/locales";
import { Box, IconButton, InputAdornment, TextField } from "@mui/material";
import { Box, FormHelperText, IconButton, InputAdornment } from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear";
import { useState } from "react";
import moment from "jalali-moment";
export default function MuiDatePicker({label, name, formik, defaultValue, minDate, maxDate, onDateChange }) {
const [pickerValue, setPickerValue] = useState(defaultValue ? new Date(defaultValue) : null);
function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error }) {
return (
<Box sx={{ position: 'relative' }}>
<Box sx={{ display: "flex", flexDirection: "column", my: 1 }}>
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps.localeText
}
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
>
<MobileDateTimePicker
key={formik.values[name]}
ampm={false}
onChange={(newValue) => {
const date = new Date(newValue);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD HH:mm");
formik.setFieldValue(name, formattedDate);
onDateChange(formattedDate);
setPickerValue(newValue);
<MobileDatePicker
value={value ? new Date(value) : null}
sx={{ width: "100%" }}
id={name}
name={name}
aria-describedby="component-helper-text"
onChange={(value) => {
const date = new Date(value);
const formattedDate = moment(date).locale("en").format("YYYY-MM-DD");
setFieldValue(name, formattedDate);
}}
minDate={minDate ? new Date(minDate) : null}
maxDate={maxDate ? new Date(maxDate) : null}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: label,
error: error,
placeholder: placeholder,
InputProps: {
endAdornment: (
<InputAdornment position="end">
@@ -40,13 +38,13 @@ export default function MuiDatePicker({label, name, formik, defaultValue, minDat
size="small"
onClick={(event) => {
event.stopPropagation();
formik.setFieldValue(name, "");
setPickerValue(null);
setFieldValue(name, "");
}}
sx={{
color: "#bfbfbf",
'&:hover': {
backgroundColor: 'rgba(189, 189, 189, 0.1)',
"&:hover": {
backgroundColor: "rgba(189, 189, 189, 0.1)",
color: "#363434",
},
}}
>
@@ -57,11 +55,13 @@ export default function MuiDatePicker({label, name, formik, defaultValue, minDat
},
},
}}
value={pickerValue}
minDate={minDate ? new Date(minDate) : null}
maxDate={maxDate ? new Date(maxDate) : null}
/>
<FormHelperText id="component-helper-text" sx={{ ml: 2, color: error ? "#d32f2f" : "unset" }}>
{helperText ? helperText : ""}
</FormHelperText>
</LocalizationProvider>
</Box>
);
}
export default MuiDatePicker;

View File

@@ -0,0 +1,37 @@
"use client";
import { FormControl, FormHelperText, InputLabel, MenuItem, OutlinedInput, Select, Typography } from "@mui/material";
function CustomSelect({ column, filterParameters, defaultFilterTranslation, setFieldValue }) {
const columnSelectOption = column.columnSelectOption;
return (
<FormControl fullWidth sx={{ marginY: 1 }}>
<InputLabel id={`label${column.id}`}>{column.header}</InputLabel>
<Select
labelId={`label${column.id}`}
id={column.id}
name={`${column.id}.value`}
value={filterParameters.value}
input={<OutlinedInput label={column.header} />}
onChange={(e) => {
setFieldValue(`${column.id}.value`, e.target.value);
}}
>
<MenuItem value="">{column.header}</MenuItem>
{columnSelectOption.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
<FormHelperText>
<Typography variant="button" sx={{ color: "#155175" }}>
نوع فیلتر: {defaultFilterTranslation}
</Typography>
</FormHelperText>
</FormControl>
);
}
export default CustomSelect;

View File

@@ -0,0 +1,60 @@
"use client";
import {
Box,
Chip,
FormControl,
FormHelperText,
InputLabel,
MenuItem,
OutlinedInput,
Select,
Typography,
} from "@mui/material";
function CustomSelectMultiple({ column, filterParameters, defaultFilterTranslation, setFieldValue }) {
const columnSelectOption = column.columnSelectOption;
const getLabelForValue = (value) => {
const option = columnSelectOption.find((opt) => opt.value === value);
return option ? option.label : value;
};
return (
<FormControl fullWidth sx={{ marginY: 1 }}>
<InputLabel htmlFor="uncontrolled-native" id={`label${column.id}`}>
{column.header}
</InputLabel>
<Select
labelId={`label${column.id}`}
id={column.id}
value={filterParameters.value}
multiple
onChange={(e) => {
setFieldValue(`${column.id}.value`, e.target.value);
}}
renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={getLabelForValue(value)} />
))}
</Box>
)}
input={<OutlinedInput label={column.header} />}
>
{columnSelectOption.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
<FormHelperText>
<Typography variant="button" sx={{ color: "#155175" }}>
نوع فیلتر: {defaultFilterTranslation}
</Typography>
</FormHelperText>
</FormControl>
);
}
export default CustomSelectMultiple;

View File

@@ -0,0 +1,40 @@
import { InputAdornment, TextField, Typography } from "@mui/material";
import FilterListIcon from "@mui/icons-material/FilterList";
function CustomTextField({
column,
filterParameters,
defaultFilterTranslation,
handleOpenFilterBox,
handleChange,
handleBlur,
}) {
return (
<TextField
id={column.id}
name={column.id}
label={column.header}
value={filterParameters.value}
onChange={handleChange}
onBlur={handleBlur}
fullWidth
helperText={
<Typography variant="button" sx={{ color: "#155175" }}>
نوع فیلتر: {defaultFilterTranslation}
</Typography>
}
variant="outlined"
size="normal"
sx={{ marginY: 1 }}
InputProps={{
endAdornment: (
<InputAdornment position="end" sx={{ cursor: "pointer" }} onClick={handleOpenFilterBox}>
<FilterListIcon />
</InputAdornment>
),
}}
/>
);
}
export default CustomTextField;

View File

@@ -0,0 +1,59 @@
import { Box, InputAdornment, TextField, Typography } from "@mui/material";
import FilterListIcon from "@mui/icons-material/FilterList";
function CustomTextFieldRange({
column,
filterParameters,
defaultFilterTranslation,
handleOpenFilterBox,
handleChange,
handleBlur,
errors,
touched,
}) {
return (
<Box sx={{ display: "flex" }}>
<TextField
id={`${column.id}.value[0]`}
name={`${column.id}.value[0]`}
onChange={handleChange}
onBlur={handleBlur}
label={<Typography>از {column.header}</Typography>}
value={filterParameters.value[0]}
fullWidth
error={touched?.[`${column.id}`]?.value && Boolean(errors?.[`${column.id}`]?.value)}
helperText={
<Typography variant="button" sx={{ color: "#155175" }}>
نوع فیلتر: {defaultFilterTranslation}
</Typography>
}
variant="outlined"
size="normal"
sx={{ marginY: 1, marginRight: 1 }}
/>
<TextField
id={`${column.id}.value[1]`}
name={`${column.id}.value[1]`}
onChange={handleChange}
onBlur={handleBlur}
error={touched?.[`${column.id}`]?.value && Boolean(errors?.[`${column.id}`]?.value)}
helperText={touched?.[`${column.id}`]?.value ? errors?.[`${column.id}`]?.value : null}
label={<Typography>تا {column.header}</Typography>}
value={filterParameters.value[1]}
fullWidth
variant="outlined"
size="normal"
sx={{ marginY: 1 }}
InputProps={{
endAdornment: (
<InputAdornment position="end" sx={{ cursor: "pointer" }} onClick={handleOpenFilterBox}>
<FilterListIcon />
</InputAdornment>
),
}}
/>
</Box>
);
}
export default CustomTextFieldRange;

View File

@@ -0,0 +1,25 @@
"use client";
import { useState } from "react";
import FilterButton from "@/core/components/DataTable/filter/FilterButton";
import FilterBody from "@/core/components/DataTable/filter/FilterBody";
function FilterColumn({ columns, user_id, page_name, table_name }) {
const [open, setOpen] = useState(false);
return (
<>
<FilterButton drawerState={open} setDrawerState={setOpen} />
<FilterBody
columns={columns}
drawerState={open}
setDrawerState={setOpen}
user_id={user_id}
page_name={page_name}
table_name={table_name}
/>
</>
);
}
export default FilterColumn;

View File

@@ -2,19 +2,10 @@ import { parseFromValuesOrFunc } from "@/core/utils/utils";
import { TableHead } from "@mui/material";
import DataTable_TableHeadRow from "./TableHeadRow";
const DataTable_TableHead = ({
columnVirtualizer,
table,
...rest
}) => {
const DataTable_TableHead = ({ columnVirtualizer, table, ...rest }) => {
const {
getState,
options: {
enableStickyHeader,
layoutMode,
muiTableHeadProps,
positionToolbarAlertBanner,
},
options: { enableStickyHeader, layoutMode, muiTableHeadProps, positionToolbarAlertBanner },
refs: { tableHeadRef },
} = table;
const { isFullScreen, showAlertBanner } = getState();
@@ -37,27 +28,24 @@ const DataTable_TableHead = ({
}
}}
sx={(theme) => ({
display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
display: layoutMode?.startsWith("grid") ? "grid" : undefined,
opacity: 0.97,
position: stickyHeader ? 'sticky' : 'relative',
top: stickyHeader && layoutMode?.startsWith('grid') ? 0 : undefined,
position: stickyHeader ? "sticky" : "relative",
top: stickyHeader && layoutMode?.startsWith("grid") ? 0 : undefined,
zIndex: stickyHeader ? 2 : undefined,
...(parseFromValuesOrFunc(tableHeadProps?.sx, theme)),
...parseFromValuesOrFunc(tableHeadProps?.sx, theme),
})}
>
{table
.getHeaderGroups()
.map((headerGroup) => (
<DataTable_TableHeadRow
columnVirtualizer={columnVirtualizer}
headerGroup={headerGroup}
key={headerGroup.id}
table={table}
/>
))
}
{table.getHeaderGroups().map((headerGroup, index) => (
<DataTable_TableHeadRow
columnVirtualizer={columnVirtualizer}
headerGroup={headerGroup}
key={headerGroup.id}
table={table}
index={index}
/>
))}
</TableHead>
);
}
export default DataTable_TableHead
};
export default DataTable_TableHead;

View File

@@ -4,13 +4,7 @@ import { Box, TableCell } from "@mui/material";
import { MRT_TableHeadCellSortLabel } from "material-react-table";
import { useMemo } from "react";
const DataTable_TableHeadCell = ({
columnVirtualizer,
header,
staticColumnIndex,
table,
...rest
}) => {
const DataTable_TableHeadCell = ({ columnVirtualizer, header, staticColumnIndex, table, ...rest }) => {
const theme = useTheme();
const {
getState,
@@ -30,14 +24,7 @@ const DataTable_TableHeadCell = ({
refs: { tableHeadCellRefs },
setHoveredColumn,
} = table;
const {
columnSizingInfo,
density,
draggingColumn,
grouping,
hoveredColumn,
showColumnFilters,
} = getState();
const { columnSizingInfo, density, draggingColumn, grouping, hoveredColumn, showColumnFilters } = getState();
const { column } = header;
const { columnDef } = column;
const { columnDefType } = columnDef;
@@ -51,23 +38,17 @@ const DataTable_TableHeadCell = ({
...rest,
};
const isColumnPinned =
enableColumnPinning &&
columnDef.columnDefType !== 'group' &&
column.getIsPinned();
const isColumnPinned = enableColumnPinning && columnDef.columnDefType !== "group" && column.getIsPinned();
const showColumnActions =
(enableColumnActions || columnDef.enableColumnActions) &&
columnDef.enableColumnActions !== false;
(enableColumnActions || columnDef.enableColumnActions) && columnDef.enableColumnActions !== false;
const showDragHandle =
enableColumnDragging !== false &&
columnDef.enableColumnDragging !== false &&
(enableColumnDragging ||
(enableColumnOrdering && columnDef.enableColumnOrdering !== false) ||
(enableGrouping &&
columnDef.enableGrouping !== false &&
!grouping.includes(column.id)));
(enableGrouping && columnDef.enableGrouping !== false && !grouping.includes(column.id)));
const headerPL = useMemo(() => {
let pl = 0;
@@ -80,41 +61,37 @@ const DataTable_TableHeadCell = ({
const draggingBorders = useMemo(() => {
const showResizeBorder =
columnSizingInfo.isResizingColumn === column.id &&
columnResizeMode === 'onChange' &&
columnResizeMode === "onChange" &&
!header.subHeaders.length;
const borderStyle = showResizeBorder
? `2px solid ${draggingBorderColor} !important`
: draggingColumn?.id === column.id
? `1px dashed ${theme.palette.grey[500]}`
: hoveredColumn?.id === column.id
? `2px dashed ${draggingBorderColor}`
: undefined;
? `1px dashed ${theme.palette.grey[500]}`
: hoveredColumn?.id === column.id
? `2px dashed ${draggingBorderColor}`
: undefined;
if (showResizeBorder) {
return columnResizeDirection === 'ltr'
? { borderRight: borderStyle }
: { borderLeft: borderStyle };
return columnResizeDirection === "ltr" ? { borderRight: borderStyle } : { borderLeft: borderStyle };
}
const draggingBorders = borderStyle
? {
borderLeft: borderStyle,
borderRight: borderStyle,
borderTop: borderStyle,
}
borderLeft: borderStyle,
borderRight: borderStyle,
borderTop: borderStyle,
}
: undefined;
return draggingBorders;
}, [draggingColumn, hoveredColumn, columnSizingInfo.isResizingColumn]);
const handleDragEnter = (_e) => {
if (enableGrouping && hoveredColumn?.id === 'drop-zone') {
if (enableGrouping && hoveredColumn?.id === "drop-zone") {
setHoveredColumn(null);
}
if (enableColumnOrdering && draggingColumn && columnDefType !== 'group') {
setHoveredColumn(
columnDef.enableColumnOrdering !== false ? column : null,
);
if (enableColumnOrdering && draggingColumn && columnDefType !== "group") {
setHoveredColumn(columnDef.enableColumnOrdering !== false ? column : null);
}
};
@@ -124,7 +101,6 @@ const DataTable_TableHeadCell = ({
}
};
const HeaderElement =
parseFromValuesOrFunc(columnDef.Header, {
column,
@@ -132,12 +108,9 @@ const DataTable_TableHeadCell = ({
table,
}) ?? columnDef.header;
const columnRelativeDepth = header.depth - column.depth;
if (
columnRelativeDepth > 1
) {
if (columnRelativeDepth > 1) {
return null;
}
@@ -149,13 +122,7 @@ const DataTable_TableHeadCell = ({
return (
<TableCell
align={
columnDefType === 'group'
? 'center'
: theme.direction === 'rtl'
? 'right'
: 'left'
}
align={columnDefType === "group" ? "center" : theme.direction === "rtl" ? "right" : "left"}
colSpan={header.colSpan}
rowSpan={rowSpan}
data-index={staticColumnIndex}
@@ -165,45 +132,40 @@ const DataTable_TableHeadCell = ({
ref={(node) => {
if (node) {
tableHeadCellRefs.current[column.id] = node;
if (columnDefType !== 'group') {
if (columnDefType !== "group") {
columnVirtualizer?.measureElement?.(node);
}
}
}}
{...tableCellProps}
sx={(theme) => ({
'& :hover': {
'.MuiButtonBase-root': {
"& :hover": {
".MuiButtonBase-root": {
opacity: 1,
},
},
flexDirection: layoutMode?.startsWith('grid') ? 'column' : undefined,
fontWeight: 'bold',
overflow: 'visible',
flexDirection: layoutMode?.startsWith("grid") ? "column" : undefined,
fontWeight: "bold",
overflow: "visible",
p:
density === 'compact'
? '0.5rem'
: density === 'comfortable'
? columnDefType === 'display'
? '0.75rem'
: '1rem'
: columnDefType === 'display'
? '1rem 1.25rem'
: '1.5rem',
pb:
columnDefType === 'display'
? 0
: showColumnFilters || density === 'compact'
? '0.4rem'
: '0.6rem',
density === "compact"
? "0.5rem"
: density === "comfortable"
? columnDefType === "display"
? "0.75rem"
: "1rem"
: columnDefType === "display"
? "1rem 1.25rem"
: "1.5rem",
pb: columnDefType === "display" ? 0 : showColumnFilters || density === "compact" ? "0.4rem" : "0.6rem",
pt:
columnDefType === 'group' || density === 'compact'
? '0.25rem'
: density === 'comfortable'
? '.75rem'
: '1.25rem',
userSelect: enableMultiSort && column.getCanSort() ? 'none' : undefined,
verticalAlign: 'middle',
columnDefType === "group" || density === "compact"
? "0.25rem"
: density === "comfortable"
? ".75rem"
: "1.25rem",
userSelect: enableMultiSort && column.getCanSort() ? "none" : undefined,
verticalAlign: "middle",
...getCommonMRTCellStyles({
column,
header,
@@ -212,72 +174,65 @@ const DataTable_TableHeadCell = ({
theme,
}),
...draggingBorders,
}
)}
})}
>
{tableCellProps.children ?? (
<Box
className="Mui-TableHeadCell-Content"
sx={{
alignItems: 'center',
display: 'flex',
flexDirection:
tableCellProps?.align === 'right' ? 'row-reverse' : 'row',
alignItems: "center",
display: "flex",
flexDirection: tableCellProps?.align === "right" ? "row-reverse" : "row",
justifyContent:
columnDefType === 'group' ||
tableCellProps?.align === 'center'
? 'center'
columnDefType === "group" || tableCellProps?.align === "center"
? "center"
: column.getCanResize()
? 'space-between'
: 'flex-start',
position: 'relative',
width: '100%',
? "space-between"
: "flex-start",
position: "relative",
width: "100%",
}}
>
<Box
className="Mui-TableHeadCell-Content-Labels"
onClick={column.getToggleSortingHandler()}
sx={{
alignItems: 'center',
cursor:
column.getCanSort() && columnDefType !== 'group'
? 'pointer'
: undefined,
display: 'flex',
flexDirection:
tableCellProps?.align === 'right' ? 'row-reverse' : 'row',
overflow: columnDefType === 'data' ? 'hidden' : undefined,
pl:
tableCellProps?.align === 'center'
? `${headerPL}rem`
: undefined,
alignItems: "center",
cursor: column.getCanSort() && columnDefType !== "group" ? "pointer" : undefined,
display: "flex",
flexDirection: tableCellProps?.align === "right" ? "row-reverse" : "row",
overflow: columnDefType === "data" ? "hidden" : undefined,
pl: tableCellProps?.align === "center" ? `${headerPL}rem` : undefined,
}}
>
<Box
className="Mui-TableHeadCell-Content-Wrapper"
sx={{
'&:hover': {
textOverflow: 'clip',
"&:hover": {
textOverflow: "clip",
},
minWidth: `${Math.min(columnDef.header?.length ?? 0, 4)}ch`,
overflow: columnDefType === 'data' ? 'hidden' : undefined,
textOverflow: 'ellipsis',
whiteSpace:
(columnDef.header?.length ?? 0) < 20
? 'nowrap'
: 'normal',
overflow: columnDefType === "data" ? "hidden" : undefined,
textOverflow: "ellipsis",
color: "#fff",
fontSize: "14px",
fontWeight: "500",
whiteSpace: (columnDef.header?.length ?? 0) < 20 ? "nowrap" : "normal",
}}
>
{HeaderElement}
</Box>
{(column.getCanSort() && columnDefType !== 'group') && (
<MRT_TableHeadCellSortLabel sx={{ width: 20 }} header={header} table={table} />
{column.getCanSort() && columnDefType !== "group" && (
<MRT_TableHeadCellSortLabel
sx={{ width: 20, color: "#fff" }}
header={header}
table={table}
/>
)}
</Box>
</Box>
)}
</TableCell>
);
}
export default DataTable_TableHeadCell;
};
export default DataTable_TableHeadCell;

View File

@@ -6,19 +6,15 @@ const DataTable_TableHeadRow = ({
columnVirtualizer,
headerGroup,
table,
index, // Add index prop
...rest
}) => {
const {
options: {
enableStickyHeader,
layoutMode,
mrtTheme: { baseBackgroundColor },
muiTableHeadRowProps,
},
options: { enableStickyHeader, layoutMode, muiTableHeadRowProps },
} = table;
const { virtualColumns, virtualPaddingLeft, virtualPaddingRight } = columnVirtualizer ?? {};
const { virtualColumns, virtualPaddingLeft, virtualPaddingRight } =
columnVirtualizer ?? {};
const backgroundColor = index % 2 === 0 ? "#015688" : "#ff5c0f";
const tableRowProps = {
...parseFromValuesOrFunc(muiTableHeadRowProps, {
@@ -26,51 +22,37 @@ const DataTable_TableHeadRow = ({
table,
}),
...rest,
sx: (theme) => ({
// Access theme from the sx function
...(parseFromValuesOrFunc(muiTableHeadRowProps?.sx, theme) || {}),
backgroundColor, // Apply background color
display: layoutMode?.startsWith("grid") ? "flex" : undefined,
position: enableStickyHeader && layoutMode === "semantic" ? "sticky" : "relative",
top: 0,
}),
};
return (
<TableRow
{...tableRowProps}
sx={(theme) => ({
backgroundColor: baseBackgroundColor,
display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
position:
enableStickyHeader && layoutMode === 'semantic'
? 'sticky'
: 'relative',
top: 0,
...(parseFromValuesOrFunc(tableRowProps?.sx, theme)),
<TableRow {...tableRowProps}>
{virtualPaddingLeft && <th style={{ display: "flex", width: virtualPaddingLeft }} />}
{(virtualColumns ?? headerGroup.headers).map((headerOrVirtualHeader, staticColumnIndex) => {
const header = columnVirtualizer
? headerGroup.headers[headerOrVirtualHeader.index]
: headerOrVirtualHeader;
return header ? (
<DataTable_TableHeadCell
columnVirtualizer={columnVirtualizer}
header={header}
key={header.id}
staticColumnIndex={staticColumnIndex}
table={table}
/>
) : null;
})}
>
{virtualPaddingLeft ? (
<th style={{ display: 'flex', width: virtualPaddingLeft }} />
) : null}
{(virtualColumns ?? headerGroup.headers).map(
(headerOrVirtualHeader, staticColumnIndex) => {
let header = headerOrVirtualHeader;
if (columnVirtualizer) {
staticColumnIndex = (headerOrVirtualHeader)
.index;
header = headerGroup.headers[staticColumnIndex];
}
return header ? (
<DataTable_TableHeadCell
columnVirtualizer={columnVirtualizer}
header={header}
key={header.id}
staticColumnIndex={staticColumnIndex}
table={table}
/>
) : null;
},
)}
{virtualPaddingRight ? (
<th style={{ display: 'flex', width: virtualPaddingRight }} />
) : null}
{virtualPaddingRight && <th style={{ display: "flex", width: virtualPaddingRight }} />}
</TableRow>
);
};
}
export default DataTable_TableHeadRow
export default DataTable_TableHeadRow;

View File

@@ -1,123 +1,12 @@
import { useMaterialReactTable } from "material-react-table";
import DataTable_Main from "./Main";
import { FA_DATATABLE_LOCALIZATION } from "./localization/fa/datatable";
import CustomToolbar from "@/components/CustomToolbar";
import useTableSetting from "@/lib/hooks/useTableSetting";
import {flattenArrayOfObjects} from "@/core/utils/flattenArrayOfObjects";
const data = [
{
name: {
firstName: 'John',
lastName: 'Doe',
},
address: '261 Erdman Ford',
city: 'East Daphne',
state: 'Kentucky',
},
{
name: {
firstName: 'Jane',
lastName: 'Doe',
},
address: '769 Dominic Grove',
city: 'Columbus',
state: 'Ohio',
},
{
name: {
firstName: 'Joe',
lastName: 'Doe',
},
address: '566 Brakus Inlet',
city: 'South Linda',
state: 'West Virginia',
},
{
name: {
firstName: 'Kevin',
lastName: 'Vandy',
},
address: '722 Emie Stream',
city: 'Lincoln',
state: 'Nebraska',
},
{
name: {
firstName: 'Joshua',
lastName: 'Rolluffs',
},
address: '32188 Larkin Turnpike',
city: 'Charleston',
state: 'South Carolina',
},
];
import DataTableProvider from "@/lib/contexts/DataTable";
const DataTable = (props) => {
const { userId, pageName, tableName, columns, initialStateProps } = props
const { settingStore, hideAction, sortAction } = useTableSetting();
const flatColumns = flattenArrayOfObjects(columns,'columns')
const onColumnVisibilityChange = (event) => {
const settingValue = event();
hideAction(userId, pageName, tableName, settingValue, flatColumns);
};
const onSortingChange = (event) => {
const settingValue = event(settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []);
sortAction(userId, pageName, tableName, settingValue, flatColumns);
}
const table = useMaterialReactTable({
localization: FA_DATATABLE_LOCALIZATION,
columns,
data: data,
// manualSorting: true,
renderTopToolbarCustomActions: ({ table }) => (<>
<CustomToolbar
columns={flatColumns}
/>
</>),
initialState: {
density: 'compact',
columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || [],
...initialStateProps
},
state: {
columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
},
muiTableHeadProps: {
sx: {
borderTop: "1px solid #e1e1e1",
borderBottom: "2px solid #e1e1e1",
}
},
muiTableHeadCellProps: {
sx: {
color: "primary.main",
borderLeft: "1px solid #e1e1e1",
"&:first-of-type": {
borderLeft: "unset"
}
},
},
muiTableBodyCellProps: {
sx: {
borderLeft: "1px solid #e1e1e1",
"&:first-of-type": {
borderLeft: "unset"
}
},
},
onColumnVisibilityChange: onColumnVisibilityChange,
onSortingChange: onSortingChange,
...props
})
return <DataTable_Main table={table} />;
return (
<DataTableProvider>
<DataTable_Main {...props} />
</DataTableProvider>
);
};
export default DataTable;

View File

@@ -67,8 +67,7 @@ export const FA_DATATABLE_LOCALIZATION = {
rowsPerPage: "ردیف در هر صفحه",
save: "ذخیره",
search: "جستجو",
selectedCountOfRowCountRowsSelected:
"{selectedCount} از {rowCount} ردیف انتخاب شده",
selectedCountOfRowCountRowsSelected: "{selectedCount} از {rowCount} ردیف انتخاب شده",
select: "انتخاب",
showAll: "نمایش همه",
showAllColumns: "نمایش همه ستون‌ها",
@@ -89,4 +88,4 @@ export const FA_DATATABLE_LOCALIZATION = {
unpin: "رها کردن",
unpinAll: "رها کردن همه",
unsorted: "بدون مرتب سازی",
};
};

View File

@@ -1,12 +1,6 @@
import { Box, IconButton, ListItemIcon, MenuItem } from "@mui/material";
const DataTable_ActionMenuItem = ({
icon,
label,
onOpenSubMenu,
table,
...rest
}) => {
const DataTable_ActionMenuItem = ({ icon, label, onOpenSubMenu, table, ...rest }) => {
const {
options: {
icons: { ArrowRightIcon },
@@ -16,35 +10,29 @@ const DataTable_ActionMenuItem = ({
return (
<MenuItem
sx={{
alignItems: 'center',
justifyContent: 'space-between',
minWidth: '120px',
alignItems: "center",
justifyContent: "space-between",
minWidth: "120px",
my: 0,
py: '6px',
py: "6px",
}}
{...rest}
>
<Box
sx={{
alignItems: 'center',
display: 'flex',
alignItems: "center",
display: "flex",
}}
>
<ListItemIcon>{icon}</ListItemIcon>
{label}
</Box>
{onOpenSubMenu && (
<IconButton
onClick={onOpenSubMenu}
onMouseEnter={onOpenSubMenu}
size="small"
sx={{ p: 0 }}
>
<IconButton onClick={onOpenSubMenu} onMouseEnter={onOpenSubMenu} size="small" sx={{ p: 0 }}>
<ArrowRightIcon />
</IconButton>
)}
</MenuItem>
);
}
export default DataTable_ActionMenuItem
};
export default DataTable_ActionMenuItem;

View File

@@ -2,10 +2,7 @@ import { parseFromValuesOrFunc } from "@/core/utils/utils";
import { Menu } from "@mui/material";
import DataTable_ActionMenuItem from "./ActionMenuItem";
const DataTable_CellActionMenu = ({
table,
...rest
}) => {
const DataTable_CellActionMenu = ({ table, ...rest }) => {
const {
getState,
options: {
@@ -32,12 +29,11 @@ const DataTable_CellActionMenu = ({
};
const internalMenuItems = [
(parseFromValuesOrFunc(enableClickToCopy, cell) === 'context-menu' ||
parseFromValuesOrFunc(columnDef.enableClickToCopy, cell) ===
'context-menu') && (
(parseFromValuesOrFunc(enableClickToCopy, cell) === "context-menu" ||
parseFromValuesOrFunc(columnDef.enableClickToCopy, cell) === "context-menu") && (
<DataTable_ActionMenuItem
icon={<ContentCopy />}
key={'mrt-copy'}
key={"mrt-copy"}
label={localization.copy}
onClick={(event) => {
event.stopPropagation();
@@ -47,10 +43,10 @@ const DataTable_CellActionMenu = ({
table={table}
/>
),
parseFromValuesOrFunc(enableEditing, row) && editDisplayMode === 'cell' && (
parseFromValuesOrFunc(enableEditing, row) && editDisplayMode === "cell" && (
<DataTable_ActionMenuItem
icon={<EditIcon />}
key={'mrt-edit'}
key={"mrt-edit"}
label={localization.edit}
onClick={() => {
openEditingCell({ cell, table });
@@ -71,14 +67,13 @@ const DataTable_CellActionMenu = ({
};
const menuItems =
columnDef.renderCellActionMenuItems?.(renderActionProps) ??
renderCellActionMenuItems?.(renderActionProps);
columnDef.renderCellActionMenuItems?.(renderActionProps) ?? renderCellActionMenuItems?.(renderActionProps);
return (
(!!menuItems?.length || !!internalMenuItems?.length) && (
<Menu
MenuListProps={{
dense: density === 'compact',
dense: density === "compact",
sx: {
backgroundColor: menuBackgroundColor,
},
@@ -95,6 +90,5 @@ const DataTable_CellActionMenu = ({
</Menu>
)
);
}
export default DataTable_CellActionMenu
};
export default DataTable_CellActionMenu;

View File

@@ -2,11 +2,7 @@ import { Box, Button, Divider, Menu } from "@mui/material";
import { useMemo, useState } from "react";
import DataTable_ShowHideColumnsMenuItems from "./ShowHideColumnsMenuItems";
const DataTable_ShowHideColumnsMenu = ({ anchorEl,
setAnchorEl,
table,
...rest
}) => {
const DataTable_ShowHideColumnsMenu = ({ anchorEl, setAnchorEl, table, ...rest }) => {
const {
getAllColumns,
getAllLeafColumns,
@@ -36,14 +32,11 @@ const DataTable_ShowHideColumnsMenu = ({ anchorEl,
const allColumns = useMemo(() => {
const columns = getAllColumns();
if (
columnOrder.length > 0 &&
!columns.some((col) => col.columnDef.columnDefType === 'group')
) {
if (columnOrder.length > 0 && !columns.some((col) => col.columnDef.columnDefType === "group")) {
return [
...getLeftLeafColumns(),
...Array.from(new Set(columnOrder)).map((colId) =>
getCenterLeafColumns().find((col) => col?.id === colId),
getCenterLeafColumns().find((col) => col?.id === colId)
),
...getRightLeafColumns(),
].filter(Boolean);
@@ -58,16 +51,14 @@ const DataTable_ShowHideColumnsMenu = ({ anchorEl,
getRightLeafColumns(),
]);
const isNestedColumns = allColumns.some(
(col) => col.columnDef.columnDefType === 'group',
);
const isNestedColumns = allColumns.some((col) => col.columnDef.columnDefType === "group");
const [hoveredColumn, setHoveredColumn] = useState(null);
return (
<Menu
MenuListProps={{
dense: density === 'compact',
dense: density === "compact",
sx: {
backgroundColor: menuBackgroundColor,
},
@@ -80,44 +71,29 @@ const DataTable_ShowHideColumnsMenu = ({ anchorEl,
>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
p: '0.5rem',
display: "flex",
justifyContent: "space-between",
p: "0.5rem",
pt: 0,
}}
>
{enableHiding && (
<Button
disabled={!getIsSomeColumnsVisible()}
onClick={() => handleToggleAllColumns(false)}
>
<Button disabled={!getIsSomeColumnsVisible()} onClick={() => handleToggleAllColumns(false)}>
{localization.hideAll}
</Button>
)}
{enableColumnOrdering && (
<Button
onClick={() =>
table.setColumnOrder(
getDefaultColumnOrderIds(table.options, true),
)
}
>
<Button onClick={() => table.setColumnOrder(getDefaultColumnOrderIds(table.options, true))}>
{localization.resetOrder}
</Button>
)}
{enableColumnPinning && (
<Button
disabled={!getIsSomeColumnsPinned()}
onClick={() => table.resetColumnPinning(true)}
>
<Button disabled={!getIsSomeColumnsPinned()} onClick={() => table.resetColumnPinning(true)}>
{localization.unpinAll}
</Button>
)}
{enableHiding && (
<Button
disabled={getIsAllColumnsVisible()}
onClick={() => handleToggleAllColumns(true)}
>
<Button disabled={getIsAllColumnsVisible()} onClick={() => handleToggleAllColumns(true)}>
{localization.showAll}
</Button>
)}
@@ -136,5 +112,5 @@ const DataTable_ShowHideColumnsMenu = ({ anchorEl,
))}
</Menu>
);
}
export default DataTable_ShowHideColumnsMenu
};
export default DataTable_ShowHideColumnsMenu;

View File

@@ -30,7 +30,7 @@ const DataTable_ShowHideColumnsMenuItems = ({
const switchChecked = column.getIsVisible();
const handleToggleColumnHidden = (column) => {
if (columnDefType === 'group') {
if (columnDefType === "group") {
column?.columns?.forEach?.((childColumn) => {
childColumn.toggleVisibility(!switchChecked);
});
@@ -78,29 +78,29 @@ const DataTable_ShowHideColumnsMenuItems = ({
ref={menuItemRef}
{...rest}
sx={(theme) => ({
alignItems: 'center',
justifyContent: 'flex-start',
alignItems: "center",
justifyContent: "flex-start",
my: 0,
opacity: isDragging ? 0.5 : 1,
outline: isDragging
? `2px dashed ${theme.palette.grey[500]}`
: hoveredColumn?.id === column.id
? `2px dashed ${draggingBorderColor}`
: 'none',
outlineOffset: '-2px',
? `2px dashed ${draggingBorderColor}`
: "none",
outlineOffset: "-2px",
pl: `${(column.depth + 0.5) * 2}rem`,
py: '6px',
...(parseFromValuesOrFunc(rest?.sx, theme)),
py: "6px",
...parseFromValuesOrFunc(rest?.sx, theme),
})}
>
<Box
sx={{
display: 'flex',
flexWrap: 'nowrap',
gap: '8px',
display: "flex",
flexWrap: "nowrap",
gap: "8px",
}}
>
{columnDefType !== 'group' &&
{columnDefType !== "group" &&
enableColumnOrdering &&
!isNestedColumns &&
(columnDef.enableColumnOrdering !== false ? (
@@ -110,7 +110,7 @@ const DataTable_ShowHideColumnsMenuItems = ({
table={table}
/>
) : (
<Box sx={{ width: '28px' }} />
<Box sx={{ width: "28px" }} />
))}
{enableHiding ? (
<FormControlLabel
@@ -119,15 +119,12 @@ const DataTable_ShowHideColumnsMenuItems = ({
typography: {
sx: {
mb: 0,
opacity: columnDefType !== 'display' ? 1 : 0.5,
opacity: columnDefType !== "display" ? 1 : 0.5,
},
},
}}
control={
<Tooltip
{...getCommonTooltipProps()}
title={localization.toggleVisibility}
>
<Tooltip {...getCommonTooltipProps()} title={localization.toggleVisibility}>
<Switch />
</Tooltip>
}
@@ -136,9 +133,7 @@ const DataTable_ShowHideColumnsMenuItems = ({
onChange={() => handleToggleColumnHidden(column)}
/>
) : (
<Typography sx={{ alignSelf: 'center' }}>
{columnDef.header}
</Typography>
<Typography sx={{ alignSelf: "center" }}>{columnDef.header}</Typography>
)}
</Box>
</MenuItem>
@@ -155,6 +150,5 @@ const DataTable_ShowHideColumnsMenuItems = ({
))}
</>
);
}
export default DataTable_ShowHideColumnsMenuItems;
};
export default DataTable_ShowHideColumnsMenuItems;

View File

@@ -0,0 +1,26 @@
"use client";
import { IconButton, Tooltip } from "@mui/material";
import RestartAltIcon from "@mui/icons-material/RestartAlt";
import useTableSetting from "@/lib/hooks/useTableSetting";
import { useState } from "react";
function RefactorTable() {
const { refactorAction } = useTableSetting();
const [userId, setUserId] = useState(0);
const [pageName, setPageName] = useState("testPage");
const [tableName, setTableName] = useState("testTable");
const refactor = () => {
refactorAction(userId, pageName, tableName);
};
return (
<Tooltip title="بازنشانی اطلاعات">
<IconButton onClick={refactor} aria-label="refactor table">
<RestartAltIcon />
</IconButton>
</Tooltip>
);
}
export default RefactorTable;

View File

@@ -5,74 +5,79 @@ import DataTable_TopToolbar from "../toolbar/TopToolbar";
import DataTable_TableContainer from "./TableContainer";
const DataTable_Paper = ({ table, ...rest }) => {
const {
getState,
options: {
enableBottomToolbar,
enableTopToolbar,
mrtTheme: { baseBackgroundColor },
muiTablePaperProps,
renderBottomToolbar,
renderTopToolbar,
},
refs: { tablePaperRef },
} = table;
const {
getState,
options: {
enableBottomToolbar,
enableTopToolbar,
mrtTheme: { baseBackgroundColor },
muiTablePaperProps,
renderBottomToolbar,
renderTopToolbar,
},
refs: { tablePaperRef },
} = table;
const { isFullScreen } = getState();
const { isFullScreen } = getState();
const paperProps = {
...parseFromValuesOrFunc(muiTablePaperProps, { table }),
...rest,
};
const paperProps = {
...parseFromValuesOrFunc(muiTablePaperProps, { table }),
...rest,
};
return (
<Paper
elevation={0}
{...paperProps}
ref={(ref) => {
tablePaperRef.current = ref;
if (paperProps?.ref) {
//@ts-ignore
paperProps.ref.current = ref;
}
}}
style={{
...(isFullScreen
? {
bottom: 0,
height: '100dvh',
left: 0,
margin: 0,
maxHeight: '100dvh',
maxWidth: '100dvw',
padding: 0,
position: 'fixed',
right: 0,
top: 0,
width: '100dvw',
zIndex: 999,
}
: {}),
...paperProps?.style,
}}
sx={(theme) => ({
backgroundColor: baseBackgroundColor,
backgroundImage: 'unset',
overflow: 'hidden',
transition: 'all 100ms ease-in-out',
...(parseFromValuesOrFunc(paperProps?.sx, theme)),
})}
>
{enableTopToolbar &&
(parseFromValuesOrFunc(renderTopToolbar, { table }) ?? (
<DataTable_TopToolbar table={table} />
))}
<DataTable_TableContainer table={table} />
{enableBottomToolbar &&
(parseFromValuesOrFunc(renderBottomToolbar, { table }) ?? (
<DataTable_BottomToolbar table={table} />
))}
</Paper>
);
return (
<Paper
elevation={0}
{...paperProps}
ref={(ref) => {
tablePaperRef.current = ref;
if (paperProps?.ref) {
//@ts-ignore
paperProps.ref.current = ref;
}
}}
style={{
...(isFullScreen
? {
bottom: 0,
height: "100dvh",
left: 0,
margin: 0,
maxHeight: "100dvh",
maxWidth: "100dvw",
padding: 0,
position: "fixed",
right: 0,
top: 0,
width: "100dvw",
zIndex: 999,
}
: {}),
...paperProps?.style,
}}
sx={(theme) => ({
backgroundColor: baseBackgroundColor,
backgroundImage: "unset",
overflow: "hidden",
transition: "all 100ms ease-in-out",
...parseFromValuesOrFunc(paperProps?.sx, theme),
})}
>
{enableTopToolbar &&
(parseFromValuesOrFunc(renderTopToolbar, { table }) ?? (
<DataTable_TopToolbar
table={table}
columns={paperProps.columns}
table_url={paperProps.table_url}
user_id={paperProps.user_id}
page_name={paperProps.page_name}
table_name={paperProps.table_name}
/>
))}
<DataTable_TableContainer table={table} />
{enableBottomToolbar &&
(parseFromValuesOrFunc(renderBottomToolbar, { table }) ?? <DataTable_BottomToolbar table={table} />)}
</Paper>
);
};
export default DataTable_Paper;

View File

@@ -5,10 +5,7 @@ import { useMemo } from "react";
import DataTable_TableBody, { Memo_DataTable_TableBody } from "../body/TableBody";
import DataTable_TableHead from "../head/TableHead";
const DataTable_Table = ({
table,
...rest
}) => {
const DataTable_Table = ({ table, ...rest }) => {
const {
getFlatHeaders,
getState,
@@ -23,8 +20,7 @@ const DataTable_Table = ({
renderCaption,
},
} = table;
const { columnSizing, columnSizingInfo, columnVisibility, isFullScreen } =
getState();
const { columnSizing, columnSizingInfo, columnVisibility, isFullScreen } = getState();
const tableProps = {
...parseFromValuesOrFunc(muiTableProps, { table }),
@@ -58,14 +54,14 @@ const DataTable_Table = ({
{...tableProps}
style={{ ...columnSizeVars, ...tableProps?.style }}
sx={(theme) => ({
display: layoutMode?.startsWith('grid') ? 'grid' : undefined,
position: 'relative',
...(parseFromValuesOrFunc(tableProps?.sx, theme)),
display: layoutMode?.startsWith("grid") ? "grid" : undefined,
position: "relative",
...parseFromValuesOrFunc(tableProps?.sx, theme),
})}
>
{!!Caption && <caption>{Caption}</caption>}
{enableTableHead && <DataTable_TableHead {...commonTableGroupProps} />}
{memoMode === 'table-body' || columnSizingInfo.isResizingColumn ? (
{memoMode === "table-body" || columnSizingInfo.isResizingColumn ? (
<Memo_DataTable_TableBody {...commonTableGroupProps} />
) : (
<DataTable_TableBody {...commonTableGroupProps} />
@@ -73,5 +69,5 @@ const DataTable_Table = ({
{/* {enableTableFooter && <MRT_TableFooter {...commonTableGroupProps} />} */}
</Table>
);
}
export default DataTable_Table
};
export default DataTable_Table;

View File

@@ -5,31 +5,17 @@ import DataTable_CellActionMenu from "../menus/CellActionMenu";
import DataTable_Table from "./Table";
import DataTable_TableLoadingOverlay from "./TableLoadingOverlay";
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
const DataTable_TableContainer = ({
table,
...rest
}) => {
const DataTable_TableContainer = ({ table, ...rest }) => {
const {
getState,
options: {
enableCellActions,
enableStickyHeader,
muiTableContainerProps,
},
options: { enableCellActions, enableStickyHeader, muiTableContainerProps },
refs: { bottomToolbarRef, tableContainerRef, topToolbarRef },
} = table;
const {
actionCell,
isFullScreen,
isLoading,
showLoadingOverlay,
} = getState();
const { actionCell, isFullScreen, isLoading, showLoadingOverlay } = getState();
const loading =
showLoadingOverlay !== false && (isLoading || showLoadingOverlay);
const loading = showLoadingOverlay !== false && (isLoading || showLoadingOverlay);
const [totalToolbarHeight, setTotalToolbarHeight] = useState(0);
@@ -41,15 +27,9 @@ const DataTable_TableContainer = ({
};
useIsomorphicLayoutEffect(() => {
const topToolbarHeight =
typeof document !== 'undefined'
? topToolbarRef.current?.offsetHeight ?? 0
: 0;
const topToolbarHeight = typeof document !== "undefined" ? topToolbarRef.current?.offsetHeight ?? 0 : 0;
const bottomToolbarHeight =
typeof document !== 'undefined'
? bottomToolbarRef?.current?.offsetHeight ?? 0
: 0;
const bottomToolbarHeight = typeof document !== "undefined" ? bottomToolbarRef?.current?.offsetHeight ?? 0 : 0;
setTotalToolbarHeight(topToolbarHeight + bottomToolbarHeight);
});
@@ -57,7 +37,7 @@ const DataTable_TableContainer = ({
return (
<TableContainer
aria-busy={loading}
aria-describedby={loading ? 'mrt-progress' : undefined}
aria-describedby={loading ? "mrt-progress" : undefined}
{...tableContainerProps}
ref={(node) => {
if (node) {
@@ -69,19 +49,17 @@ const DataTable_TableContainer = ({
}
}}
style={{
maxHeight: isFullScreen
? `calc(100vh - ${totalToolbarHeight}px)`
: undefined,
maxHeight: isFullScreen ? `calc(100vh - ${totalToolbarHeight}px)` : undefined,
...tableContainerProps?.style,
}}
sx={(theme) => ({
maxHeight: enableStickyHeader
? `clamp(350px, calc(100vh - ${totalToolbarHeight}px), 9999px)`
: undefined,
maxWidth: '100%',
overflow: 'auto',
position: 'relative',
...(parseFromValuesOrFunc(tableContainerProps?.sx, theme)),
maxWidth: "100%",
overflow: "auto",
position: "relative",
...parseFromValuesOrFunc(tableContainerProps?.sx, theme),
})}
>
{loading ? <DataTable_TableLoadingOverlay table={table} /> : null}
@@ -89,6 +67,5 @@ const DataTable_TableContainer = ({
{enableCellActions && actionCell && <DataTable_CellActionMenu table={table} />}
</TableContainer>
);
}
export default DataTable_TableContainer
};
export default DataTable_TableContainer;

View File

@@ -1,9 +1,6 @@
import { Box, CircularProgress } from "@mui/material";
const DataTable_TableLoadingOverlay = ({
table,
...rest
}) => {
const DataTable_TableLoadingOverlay = ({ table, ...rest }) => {
const {
options: {
localization,
@@ -20,17 +17,17 @@ const DataTable_TableLoadingOverlay = ({
return (
<Box
sx={{
alignItems: 'center',
alignItems: "center",
backgroundColor: alpha(baseBackgroundColor, 0.5),
bottom: 0,
display: 'flex',
justifyContent: 'center',
display: "flex",
justifyContent: "center",
left: 0,
maxHeight: '100vh',
position: 'absolute',
maxHeight: "100vh",
position: "absolute",
right: 0,
top: 0,
width: '100%',
width: "100%",
zIndex: 3,
}}
>
@@ -43,6 +40,5 @@ const DataTable_TableLoadingOverlay = ({
)}
</Box>
);
}
export default DataTable_TableLoadingOverlay
};
export default DataTable_TableLoadingOverlay;

View File

@@ -3,23 +3,15 @@ import { Box, alpha, useMediaQuery } from "@mui/material";
import DataTable_LinearProgressBar from "./LinearProgressBar";
import DataTable_TablePagination from "./TablePagination";
const DataTable_BottomToolbar = ({
table,
...rest
}) => {
const DataTable_BottomToolbar = ({ table, ...rest }) => {
const {
getState,
options: {
enablePagination,
muiBottomToolbarProps,
positionPagination,
renderBottomToolbarCustomActions,
},
options: { enablePagination, muiBottomToolbarProps, positionPagination, renderBottomToolbarCustomActions },
refs: { bottomToolbarRef },
} = table;
const { isFullScreen } = getState();
const isMobile = useMediaQuery('(max-width:720px)');
const isMobile = useMediaQuery("(max-width:720px)");
const toolbarProps = {
...parseFromValuesOrFunc(muiBottomToolbarProps, { table }),
...rest,
@@ -40,49 +32,41 @@ const DataTable_BottomToolbar = ({
}}
sx={(theme) => ({
...getCommonToolbarStyles({ table, theme }),
bottom: isFullScreen ? '0' : undefined,
boxShadow: `0 1px 2px -1px ${alpha(
theme.palette.grey[700],
0.5,
)} inset`,
bottom: isFullScreen ? "0" : undefined,
boxShadow: `0 1px 2px -1px ${alpha(theme.palette.grey[700], 0.5)} inset`,
left: 0,
position: isFullScreen ? 'fixed' : 'relative',
position: isFullScreen ? "fixed" : "relative",
right: 0,
...(parseFromValuesOrFunc(toolbarProps?.sx, theme)),
...parseFromValuesOrFunc(toolbarProps?.sx, theme),
})}
>
<DataTable_LinearProgressBar isTopToolbar={false} table={table} />
<Box
sx={{
alignItems: 'center',
boxSizing: 'border-box',
display: 'flex',
justifyContent: 'space-between',
p: '0.5rem',
width: '100%',
alignItems: "center",
boxSizing: "border-box",
display: "flex",
justifyContent: "space-between",
p: "0.5rem",
width: "100%",
}}
>
{renderBottomToolbarCustomActions ? (
renderBottomToolbarCustomActions({ table })
) : (
<span />
)}
{renderBottomToolbarCustomActions ? renderBottomToolbarCustomActions({ table }) : <span />}
<Box
sx={{
display: 'flex',
justifyContent: 'flex-end',
position: stackAlertBanner ? 'relative' : 'absolute',
display: "flex",
justifyContent: "flex-end",
position: stackAlertBanner ? "relative" : "absolute",
right: 0,
top: 0,
}}
>
{enablePagination &&
['both', 'bottom'].includes(positionPagination ?? '') && (
<DataTable_TablePagination position="bottom" table={table} />
)}
{enablePagination && ["both", "bottom"].includes(positionPagination ?? "") && (
<DataTable_TablePagination position="bottom" table={table} />
)}
</Box>
</Box>
</Box>
);
}
export default DataTable_BottomToolbar
};
export default DataTable_BottomToolbar;

View File

@@ -1,11 +1,7 @@
import { parseFromValuesOrFunc } from "@/core/utils/utils";
import { Collapse, LinearProgress } from "@mui/material";
const DataTable_LinearProgressBar = ({
isTopToolbar,
table,
...rest
}) => {
const DataTable_LinearProgressBar = ({ isTopToolbar, table, ...rest }) => {
const {
getState,
options: { muiLinearProgressProps },
@@ -26,19 +22,19 @@ const DataTable_LinearProgressBar = ({
mountOnEnter
sx={{
bottom: isTopToolbar ? 0 : undefined,
position: 'absolute',
position: "absolute",
top: !isTopToolbar ? 0 : undefined,
width: '100%',
width: "100%",
}}
unmountOnExit
>
<LinearProgress
aria-busy="true"
aria-label="Loading"
sx={{ position: 'relative' }}
sx={{ position: "relative" }}
{...linearProgressProps}
/>
</Collapse>
);
}
export default DataTable_LinearProgressBar
};
export default DataTable_LinearProgressBar;

View File

@@ -1,16 +1,23 @@
import { flipIconStyles, getCommonTooltipProps, parseFromValuesOrFunc } from "@/core/utils/utils";
import { useTheme } from "@emotion/react";
import { Box, IconButton, InputLabel, MenuItem, Pagination, PaginationItem, Select, Tooltip, Typography, useMediaQuery } from "@mui/material";
import {
Box,
IconButton,
InputLabel,
MenuItem,
Pagination,
PaginationItem,
Select,
Tooltip,
Typography,
useMediaQuery,
} from "@mui/material";
const defaultRowsPerPage = [5, 10, 15, 20, 25, 30, 50, 100];
const DataTable_TablePagination = ({
position = 'bottom',
table,
...rest
}) => {
const DataTable_TablePagination = ({ position = "bottom", table, ...rest }) => {
const theme = useTheme();
const isMobile = useMediaQuery('(max-width: 720px)');
const isMobile = useMediaQuery("(max-width: 720px)");
const {
getState,
@@ -64,26 +71,21 @@ const DataTable_TablePagination = ({
<Box
className="MuiTablePagination-root"
sx={{
alignItems: 'center',
display: 'flex',
flexWrap: 'wrap',
gap: '8px',
justifyContent: { md: 'space-between', sm: 'center' },
justifySelf: 'flex-end',
mt:
position === 'top' &&
enableToolbarInternalActions &&
!showGlobalFilter
? '3rem'
: undefined,
position: 'relative',
px: '8px',
py: '12px',
alignItems: "center",
display: "flex",
flexWrap: "wrap",
gap: "8px",
justifyContent: { md: "space-between", sm: "center" },
justifySelf: "flex-end",
mt: position === "top" && enableToolbarInternalActions && !showGlobalFilter ? "3rem" : undefined,
position: "relative",
px: "8px",
py: "12px",
zIndex: 2,
}}
>
{showRowsPerPage && (
<Box sx={{ alignItems: 'center', display: 'flex', gap: '8px' }}>
<Box sx={{ alignItems: "center", display: "flex", gap: "8px" }}>
<InputLabel htmlFor="mrt-rows-per-page" sx={{ mb: 0 }}>
{localization.rowsPerPage}
</InputLabel>
@@ -92,22 +94,19 @@ const DataTable_TablePagination = ({
disableUnderline
disabled={disabled}
inputProps={{
'aria-label': localization.rowsPerPage,
id: 'mrt-rows-per-page',
"aria-label": localization.rowsPerPage,
id: "mrt-rows-per-page",
}}
label={localization.rowsPerPage}
onChange={(event) =>
table.setPageSize(+(event.target.value))
}
onChange={(event) => table.setPageSize(+event.target.value)}
sx={{ mb: 0 }}
value={pageSize}
variant="standard"
{...SelectProps}
>
{rowsPerPageOptions.map((option) => {
const value = typeof option !== 'number' ? option.value : option;
const label =
typeof option !== 'number' ? option.label : `${option}`;
const value = typeof option !== "number" ? option.value : option;
const label = typeof option !== "number" ? option.label : `${option}`;
return (
SelectProps?.children ??
(SelectProps?.native ? (
@@ -124,7 +123,7 @@ const DataTable_TablePagination = ({
</Select>
</Box>
)}
{paginationDisplayMode === 'pages' ? (
{paginationDisplayMode === "pages" ? (
<Pagination
count={numberOfPages}
disabled={disabled}
@@ -145,16 +144,18 @@ const DataTable_TablePagination = ({
showLastButton={showLastButton}
{...restPaginationProps}
/>
) : paginationDisplayMode === 'default' ? (
) : paginationDisplayMode === "default" ? (
<>
<Typography
align="center"
component="span"
sx={{ m: '0 4px', minWidth: '8ch' }}
sx={{ m: "0 4px", minWidth: "8ch" }}
variant="body2"
>{`${lastRowIndex === 0 ? 0 : (firstRowIndex + 1).toLocaleString()
}-${lastRowIndex.toLocaleString()} ${localization.of
} ${totalRowCount.toLocaleString()}`}</Typography>
>{`${
lastRowIndex === 0 ? 0 : (firstRowIndex + 1).toLocaleString()
}-${lastRowIndex.toLocaleString()} ${
localization.of
} ${totalRowCount.toLocaleString()}`}</Typography>
<Box gap="xs">
{showFirstButton && (
<Tooltip {...tooltipProps} title={localization.goToFirstPage}>
@@ -213,5 +214,5 @@ const DataTable_TablePagination = ({
) : null}
</Box>
);
}
export default DataTable_TablePagination
};
export default DataTable_TablePagination;

View File

@@ -16,32 +16,29 @@ const DataTable_ToolbarInternalButtons = ({ table, ...rest }) => {
},
} = table;
return (
<Box
{...rest}
sx={(theme) => ({
alignItems: 'center',
display: 'flex',
alignItems: "center",
display: "flex",
zIndex: 3,
...(parseFromValuesOrFunc(rest?.sx, theme)),
...parseFromValuesOrFunc(rest?.sx, theme),
})}
>
{renderToolbarInternalActions?.({
table,
}) ?? (
<>
{enableFilters &&
enableColumnFilters &&
columnFilterDisplayMode !== 'popover' && (
<DataTable_ToggleFiltersButton table={table} />
)}
{(enableHiding || enableColumnOrdering || enableColumnPinning) && (
<DataTable_ShowHideColumnsButton table={table} />
)}
</>
)}
<>
{enableFilters && enableColumnFilters && columnFilterDisplayMode !== "popover" && (
<DataTable_ToggleFiltersButton table={table} />
)}
{(enableHiding || enableColumnOrdering || enableColumnPinning) && (
<DataTable_ShowHideColumnsButton table={table} />
)}
</>
)}
</Box>
);
}
export default DataTable_ToolbarInternalButtons
};
export default DataTable_ToolbarInternalButtons;

View File

@@ -3,8 +3,10 @@ import { Box, useMediaQuery } from "@mui/material";
import DataTable_LinearProgressBar from "./LinearProgressBar";
import DataTable_TablePagination from "./TablePagination";
import DataTable_ToolbarInternalButtons from "./ToolbarInternalButtons";
import FilterColumn from "@/core/components/DataTable/filter";
import RefactorTable from "@/core/components/DataTable/refactor/RefactorTable";
const DataTable_TopToolbar = ({ table }) => {
const DataTable_TopToolbar = ({ table, columns, table_url, user_id, page_name, table_name }) => {
const {
getState,
options: {
@@ -18,15 +20,12 @@ const DataTable_TopToolbar = ({ table }) => {
} = table;
const { isFullScreen, showGlobalFilter } = getState();
const isMobile = useMediaQuery('(max-width:720px)');
const isTablet = useMediaQuery('(max-width:1024px)');
const isMobile = useMediaQuery("(max-width:720px)");
const isTablet = useMediaQuery("(max-width:1024px)");
const toolbarProps = parseFromValuesOrFunc(muiTopToolbarProps, { table });
const stackAlertBanner =
isMobile ||
!!renderTopToolbarCustomActions ||
(showGlobalFilter && isTablet);
const stackAlertBanner = isMobile || !!renderTopToolbarCustomActions || (showGlobalFilter && isTablet);
return (
<Box
@@ -40,47 +39,54 @@ const DataTable_TopToolbar = ({ table }) => {
}}
sx={(theme) => ({
...getCommonToolbarStyles({ table, theme }),
position: isFullScreen ? 'sticky' : 'relative',
top: isFullScreen ? '0' : undefined,
...(parseFromValuesOrFunc(toolbarProps?.sx, theme)),
position: isFullScreen ? "sticky" : "relative",
top: isFullScreen ? "0" : undefined,
...parseFromValuesOrFunc(toolbarProps?.sx, theme),
})}
>
<Box
sx={{
alignItems: 'flex-start',
boxSizing: 'border-box',
display: 'flex',
gap: '0.5rem',
justifyContent: 'space-between',
p: '0.5rem',
position: stackAlertBanner ? 'relative' : 'absolute',
alignItems: "flex-start",
boxSizing: "border-box",
display: "flex",
gap: "0.5rem",
justifyContent: "space-between",
p: "0.5rem",
position: stackAlertBanner ? "relative" : "absolute",
right: 0,
top: 0,
width: '100%',
width: "100%",
}}
>
{renderTopToolbarCustomActions?.({ table }) ?? <span />}
{enableToolbarInternalActions && (
<Box
sx={{
alignItems: 'center',
display: 'flex',
flexWrap: 'wrap-reverse',
gap: '0.5rem',
justifyContent: 'flex-end',
alignItems: "center",
display: "flex",
flexWrap: "wrap-reverse",
gap: "0.5rem",
justifyContent: "flex-end",
}}
>
<RefactorTable />
<FilterColumn
columns={columns}
table_url={table_url}
user_id={user_id}
page_name={page_name}
table_name={table_name}
/>
<DataTable_ToolbarInternalButtons table={table} />
</Box>
)}
</Box>
{enablePagination &&
['both', 'top'].includes(positionPagination ?? '') && (
<DataTable_TablePagination position="top" table={table} />
)}
{enablePagination && ["both", "top"].includes(positionPagination ?? "") && (
<DataTable_TablePagination position="top" table={table} />
)}
<DataTable_LinearProgressBar isTopToolbar table={table} />
</Box>
);
}
};
export default DataTable_TopToolbar
export default DataTable_TopToolbar;

View File

@@ -0,0 +1,61 @@
"use client";
import SaveIcon from "@mui/icons-material/Save";
import { Box, Button, Typography } from "@mui/material";
import DownloadIcon from "@mui/icons-material/Download";
import { toast } from "react-toastify";
function AskForKeep({ onSaveFilter }) {
const handleDismiss = () => {
toast.dismiss({ containerId: "filtering" });
};
return (
<Box sx={{ width: "100%" }}>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
mb: 1,
}}
>
<Typography color="#155175" variant="subtitle1" sx={{ fontWeight: "500" }}>
ذخیره سازی اطلاعات
</Typography>
<SaveIcon sx={{ color: "#155175" }} />
</Box>
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography variant="caption">آیا مایل به ذخیره فیلتر های اعمال شده برای دفعات بعد هستید؟</Typography>
</Box>
<Box sx={{ display: "flex", gap: 1, mt: 1 }}>
<Button
variant="contained"
onClick={handleDismiss}
sx={{
backgroundColor: "#792626",
":hover": { backgroundColor: "#792626" },
boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
}}
>
رد کردن
</Button>
<Button
variant="contained"
component="label"
endIcon={<DownloadIcon />}
onClick={onSaveFilter}
sx={{
backgroundColor: "#155175",
":hover": { backgroundColor: "#155175" },
boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
}}
>
ذخیره فیلتر
</Button>
</Box>
</Box>
);
}
export default AskForKeep;

View File

@@ -1,19 +1,22 @@
import {Collapse, ListItem, ListItemButton, ListItemIcon, ListItemText} from "@mui/material";
import { ExpandLess, ExpandMore } from '@mui/icons-material';
import WifiIcon from '@mui/icons-material/Wifi';
import SidebarSubitems from "@/core/components/SidebarSubitems";
import { ExpandLess, ExpandMore } from "@mui/icons-material";
import { Collapse, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from "@mui/material";
import Link from "next/link";
const SidebarListItems = ({menuItem, dispatch}) => {
return(
const SidebarListItems = ({ menuItem, dispatch }) => {
return (
<>
<ListItem disablePadding sx={{p : 0}}>
<ListItemButton sx={{
'&.Mui-selected': {
backgroundColor: 'rgba(0, 0, 0, 0.2)',
},
}} selected={menuItem.selected} onClick={() => {
dispatch({type: "COLLAPSE_MENU", id: menuItem.id})
}}>
<ListItem disablePadding divider>
<ListItemButton
sx={{ py: 1, px: 0.5 }}
disableGutters
selected={menuItem.selected}
component={menuItem.type === "page" ? Link : null}
href={menuItem.type === "page" ? menuItem.route : null}
onClick={() => {
dispatch({ type: "COLLAPSE_MENU", id: menuItem.id });
}}
>
<ListItemIcon
sx={{
minWidth: 0,
@@ -21,26 +24,30 @@ const SidebarListItems = ({menuItem, dispatch}) => {
color: "primary.main",
width: 40,
height: 24,
pr: 2,
}}
>
{menuItem.icon}
</ListItemIcon>
<ListItemText primary={menuItem.id} />
{menuItem.hasSubitems ? (menuItem.showSubitems ? <ExpandLess /> : <ExpandMore />) : null }
<ListItemText primary={menuItem.label} />
{menuItem.hasSubitems ? menuItem.showSubitems ? <ExpandLess /> : <ExpandMore /> : null}
</ListItemButton>
</ListItem>
<Collapse in={menuItem.showSubitems} timeout="auto" mountOnEnter={true} unmountOnExit={true}>
{
menuItem.hasSubitems ? (menuItem.Subitems.map((subitem,index)=>{
return(
<SidebarSubitems dispatch={dispatch} key={index} subitem={subitem}/>
)
})) : null
}
{menuItem.hasSubitems ? (
<List
disablePadding
dense
sx={{
background: "#00000008",
}}
>
{menuItem.Subitems.map((subitem, index) => {
return <SidebarSubitems key={index} dispatch={dispatch} subitem={subitem} />;
})}
</List>
) : null}
</Collapse>
</>
)
}
export default SidebarListItems
);
};
export default SidebarListItems;

View File

@@ -1,170 +1,101 @@
"use client"
import {List} from "@mui/material";
"use client";
import SidebarListItems from "@/core/components/SidebarListItems";
import {useEffect, useReducer, useState} from "react";
import {usePathname} from 'next/navigation'
import SecurityIcon from '@mui/icons-material/Security';
import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
import AssessmentIcon from '@mui/icons-material/Assessment';
import AirlineSeatReclineNormalIcon from "@mui/icons-material/AirlineSeatReclineNormal";
import RepartitionIcon from '@mui/icons-material/Repartition';
const data = [
{
id: "hello",
type: "menu",
route: "/hello",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
Subitems: [
{
id: "amin",
route: "/s",
icon: <SpaceDashboardIcon sx={{width: 'inherit', height: 'inherit'}}/>,
firstName: "amin",
lastName: "ali",
Subitems: [
{
id: "shahrokh", shahrokh: "shahrokh", type: "page", selected: false, route: "/dashbssoard", icon: <AssessmentIcon sx={{width: 'inherit', height: 'inherit'}}/>
},
{
id: "shasdasdsahrokh", shahrokh: "shahroasdasdkh", type: "page", selected: false, route: "/dashboardsd", icon: <AirlineSeatReclineNormalIcon sx={{width: 'inherit', height: 'inherit'}}/>
}
],
hasSubitems: true,
showSubitems: false,
type: "menu",
selected: false,
},
{
id: "amir",
route: "/dashboarsad",
icon: <RepartitionIcon sx={{width: 'inherit', height: 'inherit'}}/>,
firstName: "amir",
lastName: "akbar",
hasSubitems: false,
showSubitems: false,
selected: false,
type: "page",
},
],
hasSubitems: true,
showSubitems: false
},
{
id: "hi",
type: "menu",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
route: "/hi",
selected: false,
Subitems: [
{
id: "ali",
route: "/dashboardss",
firstName: "ali",
lastName: "ali",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
hasSubitems: false,
showSubitems: false,
type: "page",
selected: false,
},
{
id: "akbar",
route: "/akbar",
firstName: "akbar",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
lastName: "akbar",
hasSubitems: false,
showSubitems: false,
type: "page",
selected: false,
},
],
hasSubitems: true,
showSubitems: false
},
{
id: "hoo",
type: "page",
route: "/dashboard",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
hasSubitems: false,
showSubitems: false
}
]
import { usePermissions } from "@/lib/hooks/usePermissions";
import { List } from "@mui/material";
import { usePathname } from "next/navigation";
import { useEffect, useReducer, useState } from "react";
import { filterMenuItems } from "../utils/filterMenuItems";
import { pageMenu } from "../utils/pageMenu";
function reducer(state, action) {
switch (action.type) {
case "UPDATE_MENU":
const _permissions = action.permissions || []
const filteredPageMenu = filterMenuItems(pageMenu, ['all', ..._permissions])
return filteredPageMenu
case "COLLAPSE_MENU":
return state.map((item) => action.id == item.id ? {
...item, showSubitems: !item.showSubitems
} : item)
case "COLLAPSE_SUB_ITEMS" :
return state.map((item) =>
action.id == item.id
? {
...item,
showSubitems: !item.showSubitems,
}
: item
);
case "COLLAPSE_SUB_ITEMS":
return state.map((item) => {
return item.hasSubitems ? {
...item, Subitems: item.Subitems.map((subitem) => action.id === subitem.id ? {
...subitem, showSubitems: !subitem.showSubitems
} : subitem)
} : item
})
case "COLLAPSE_SUB_SECOND_ITEMS" :
return item.hasSubitems
? {
...item,
Subitems: item.Subitems.map((subitem) =>
action.id === subitem.id
? {
...subitem,
showSubitems: !subitem.showSubitems,
}
: subitem
),
}
: item;
});
case "COLLAPSE_SUB_SECOND_ITEMS":
return state.map((item) => {
return item.hasSubitems ? {
...item, Subitems: item.Subitems.map((subitem) => {
return subitem.hasSubitems ? {
...subitem,
Subitems: subitem.Subitems.map((secondSubitem) => action.id === secondSubitem.id ? {
...secondSubitem, showSubitems: !secondSubitem.showSubitems
} : secondSubitem)
} : subitem
})
} : item
})
return item.hasSubitems
? {
...item,
Subitems: item.Subitems.map((subitem) => {
return subitem.hasSubitems
? {
...subitem,
Subitems: subitem.Subitems.map((secondSubitem) =>
action.id === secondSubitem.id
? {
...secondSubitem,
showSubitems: !secondSubitem.showSubitems,
}
: secondSubitem
),
}
: subitem;
}),
}
: item;
});
case "SELECTED":
return state.map((item) => {
return item.type === "page" ? {
...item, selected: action.route === item.route,showSubitems: item.route === action.route
} : item.Subitems && Array.isArray(item.Subitems) ? {
...item, Subitems: item.Subitems.map((subitem) => {
return subitem.type === "page" ? {
...subitem, selected: action.route === subitem.route,showSubitems: subitem.route === action.route
} : subitem.Subitems && Array.isArray(subitem.Subitems) ? {
...subitem, Subitems: subitem.Subitems.map((secondSubItem) => ({
...secondSubItem, selected: secondSubItem.route === action.route
})),showSubitems: subitem.Subitems.some((secondSubItem) => secondSubItem.route === action.route),
} : subitem
}),showSubitems: item.Subitems.some((subitem) => subitem.showSubitems),
} : item
return item.type === "page"
? {
...item,
selected: action.route === item.route,
showSubitems: item.route === action.route,
}
: item.Subitems && Array.isArray(item.Subitems)
? {
...item,
Subitems: item.Subitems.map((subitem) => {
return subitem.type === "page"
? {
...subitem,
selected: action.route === subitem.route,
showSubitems: subitem.route === action.route,
}
: subitem.Subitems && Array.isArray(subitem.Subitems)
? {
...subitem,
Subitems: subitem.Subitems.map((secondSubItem) => ({
...secondSubItem,
selected: secondSubItem.route === action.route,
})),
showSubitems: subitem.Subitems.some(
(secondSubItem) => secondSubItem.route === action.route
),
}
: subitem;
}),
showSubitems: item.Subitems.some((subitem) => subitem.showSubitems),
}
: item;
});
// case "SELECTED":
// return state.map((item) => {
// return item.type === "page" ? {
// ...item,
// selected: action.route === item.route
// } : item.Subitems && Array.isArray(item.Subitems) ? {
// ...item,
// Subitems: item.Subitems.map((subitem) => {
// const updatedSubitem = subitem.type === "page" ? {
// ...subitem,
// selected: action.route === subitem.route
// } : subitem.Subitems && Array.isArray(subitem.Subitems) ? {
// ...subitem,
// Subitems: subitem.Subitems.map((secondSubItem) => ({
// ...secondSubItem,
// selected: secondSubItem.route === action.route
// })),
// showSubitems: subitem.Subitems.some((secondSubItem) => secondSubItem.route === action.route),
// } : subitem;
//
// console.log("Updated subitem:", updatedSubitem); // Log the updated subitem
// return updatedSubitem;
// }),
// showSubitems: item.Subitems.some((subitem) => subitem.showSubitems),
// } : item
// });
default:
throw new Error();
@@ -172,30 +103,34 @@ function reducer(state, action) {
}
const SidebarMenu = () => {
const [menuItems, dispatch] = useReducer(reducer, data);
const pathname = usePathname()
const { data: userPermissions } = usePermissions()
const [menuItems, dispatch] = useReducer(reducer, pageMenu);
const pathname = usePathname();
const [selectedKey, setSelectedKey] = useState(null);
useEffect(() => {
dispatch({type: "SELECTED", route: pathname});
dispatch({ type: 'UPDATE_MENU', permissions: userPermissions });
}, [userPermissions])
useEffect(() => {
dispatch({ type: "SELECTED", route: pathname });
setSelectedKey(pathname);
}, [pathname]);
useEffect(() => {
selectedKey && document.querySelector(`[data-route="${selectedKey}"]`)?.scrollIntoView({
behavior: "smooth", block: "center"
});
selectedKey &&
document.querySelector(`[data-route="${selectedKey}"]`)?.scrollIntoView({
behavior: "smooth",
block: "center",
});
}, [selectedKey]);
return (
<List disablePadding>
{
menuItems.map((menuItem) => {
return (
<SidebarListItems dispatch={dispatch} key={menuItem.id} menuItem={menuItem}/>
)
})
}
<List disablePadding dense>
{menuItems.map((menuItem) => {
return <SidebarListItems dispatch={dispatch} key={menuItem.id} menuItem={menuItem} />;
})}
</List>
)
}
export default SidebarMenu
);
};
export default SidebarMenu;

View File

@@ -1,74 +1,51 @@
import {Collapse, List, ListItem, ListItemButton, ListItemIcon, ListItemText} from "@mui/material";
import {ExpandLess, ExpandMore} from "@mui/icons-material";
import { ExpandLess, ExpandMore } from "@mui/icons-material";
import { Collapse, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from "@mui/material";
import Link from "next/link";
const SidebarSubitems = ({subitem, dispatch}) => {
const SidebarSubitems = ({ subitem, dispatch }) => {
return (
<>
<List disablePadding>
<ListItem disablePadding>
<ListItemButton sx={{
'&.Mui-selected': {
backgroundColor: 'rgba(0, 0, 0, 0.1)',
},
}} selected={subitem.selected} onClick={() => {
dispatch({type: "COLLAPSE_SUB_ITEMS", id: subitem.id})
}}>
<ListItemIcon
sx={{
minWidth: 0,
justifyContent: "center",
color: "primary.main",
width: 40,
height: 24,
pr: 2,
}}
>
{subitem.icon}
</ListItemIcon>
<ListItemText primary={subitem.firstName}/>
{subitem.hasSubitems ? (subitem.showSubitems ? <ExpandLess/> : <ExpandMore/>) : null
}
</ListItemButton>
</ListItem>
</List>
{
subitem.hasSubitems ? (subitem.Subitems.map((subSubitem, index) => {
return (
<Collapse key={index} in={subitem.showSubitems} timeout="auto" mountOnEnter={true} unmountOnExit={true}>
<List disablePadding>
<ListItem disablePadding>
<ListItemButton
onClick={() => {
dispatch({type: "COLLAPSE_SUB_SECOND_ITEMS", id: subSubitem.id})
}}
selected={subSubitem.selected}
sx={{
'&.Mui-selected': {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
},
}}
>
<ListItemIcon
sx={{
minWidth: 0,
justifyContent: "center",
color: "primary.main",
width: 40,
height: 24,
pr: 2,
}}
>
{subSubitem.icon}
</ListItemIcon>
<ListItemText primary={subSubitem.shahrokh}/>
</ListItemButton>
</ListItem>
</List>
</Collapse>
)
})) : null
}
<ListItem disablePadding divider>
<ListItemButton
disableGutters
sx={{ py: 1, px: 0.5, pl: 1 }}
selected={subitem.selected}
component={subitem.type === "page" ? Link : null}
href={subitem.type === "page" ? subitem.route : null}
onClick={() => {
dispatch({ type: "COLLAPSE_SUB_ITEMS", id: subitem.id });
}}
>
<ListItemIcon
sx={{
minWidth: 0,
justifyContent: "center",
width: 40,
height: 24,
}}
>
{subitem.icon}
</ListItemIcon>
<ListItemText primary={subitem.label} />
{subitem.hasSubitems ? subitem.showSubitems ? <ExpandLess /> : <ExpandMore /> : null}
</ListItemButton>
</ListItem>
<Collapse in={subitem.showSubitems} timeout="auto" mountOnEnter={true} unmountOnExit={true}>
{subitem.hasSubitems ? (
<List
disablePadding
dense
sx={{
background: "#00000008",
}}
>
{subitem.Subitems.map((subSubitem, index) => {
return <SidebarSubitems key={index} dispatch={dispatch} subitem={subSubitem} />;
})}
</List>
) : null}
</Collapse>
</>
)
}
export default SidebarSubitems
);
};
export default SidebarSubitems;

View File

@@ -0,0 +1,187 @@
import { toast } from "react-toastify";
import { Box, Typography } from "@mui/material";
import { Dangerous } from "@mui/icons-material";
export const errorServerToast = (toastContainer) =>
toast.error(
() => (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Dangerous color="error" sx={{ mr: 1.6 }} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography variant="caption">{"The request failed due to an internal error."}</Typography>
</Box>
</Box>
</Box>
),
{
icon: false,
containerId: toastContainer,
autoClose: 5000,
hideProgressBar: true,
pauseOnHover: true,
closeOnClick: false,
draggable: true,
}
);
export const errorUnauthorizedToast = (toastContainer) =>
toast.error(
() => (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Dangerous color="error" sx={{ mr: 1.6 }} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography variant="caption">{"The user is not authorized to make the request."}</Typography>
</Box>
</Box>
</Box>
),
{
icon: false,
containerId: toastContainer,
autoClose: 3000,
hideProgressBar: true,
pauseOnHover: true,
closeOnClick: false,
draggable: true,
}
);
export const errorLogicToast = (message, toastContainer) =>
toast.error(
() => (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Dangerous color="error" sx={{ mr: 1.6 }} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography variant="caption">
{message ||
"The request was well-formed but was unable to be followed due to semantic errors."}
</Typography>
</Box>
</Box>
</Box>
),
{
icon: false,
containerId: toastContainer,
autoClose: false,
closeOnClick: false,
draggable: false,
}
);
export const errorValidationToast = (message, toastContainer) =>
toast.error(
() => (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Dangerous color="error" sx={{ mr: 1.6 }} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography variant="caption">
{message ||
"The request was well-formed but was unable to be followed due to semantic errors."}
</Typography>
</Box>
</Box>
</Box>
),
{
icon: false,
containerId: toastContainer,
autoClose: false,
closeOnClick: false,
draggable: false,
}
);
export const errorTooManyToast = (toastContainer) =>
toast.error(
() => (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Dangerous color="error" sx={{ mr: 1.6 }} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography variant="caption">
{"Too many requests have been sent within a given time span."}
</Typography>
</Box>
</Box>
</Box>
),
{
icon: false,
containerId: toastContainer,
autoClose: false,
closeOnClick: false,
draggable: false,
}
);
export const errorClientToast = (toastContainer) =>
toast.error(
() => (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Dangerous color="error" sx={{ mr: 1.6 }} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography variant="caption">
{
"The API request is invalid or improperly formed. Consequently, the API server could not understand the request."
}
</Typography>
</Box>
</Box>
</Box>
),
{
icon: false,
containerId: toastContainer,
autoClose: false,
closeOnClick: false,
draggable: false,
}
);

View File

@@ -0,0 +1,33 @@
import { toast } from "react-toastify";
import { Box, Typography } from "@mui/material";
import { Beenhere } from "@mui/icons-material";
export const successToast = (toastContainer) =>
toast.success(
() => (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "start",
justifyContent: "start",
}}
>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Beenhere color="success" sx={{ mr: 1.6 }} />
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Typography variant="caption">{"Your operation was successful"}</Typography>
</Box>
</Box>
</Box>
),
{
icon: false,
containerId: toastContainer,
autoClose: 3000,
hideProgressBar: true,
pauseOnHover: true,
closeOnClick: false,
draggable: true,
}
);

View File

@@ -0,0 +1,22 @@
"use client";
import { useAuth } from "@/lib/contexts/auth";
import { usePathname, useRouter } from "next/navigation";
import { useEffect } from "react";
function WithAuthMiddleware({ children }) {
const router = useRouter();
const pathName = usePathname();
const { isAuth, initAuthState } = useAuth();
useEffect(() => {
if (!initAuthState) return;
if (!isAuth) {
router.replace(`${process.env.NEXT_PUBLIC_API_URL}/login?_back=${encodeURIComponent(pathName)}`);
}
}, [isAuth, initAuthState]);
if (!initAuthState || !isAuth) return null;
return <>{children}</>;
}
export default WithAuthMiddleware;

View File

@@ -1,16 +1,16 @@
'use client'
"use client";
import createCache from "@emotion/cache";
import {prefixer} from "stylis";
import { prefixer } from "stylis";
import stylisRTLPlugin from "stylis-plugin-rtl";
import {CacheProvider} from "@emotion/react";
import { CacheProvider } from "@emotion/react";
const cache = createCache({
key: 'mui-rtl',
key: "mui-rtl",
stylisPlugins: [prefixer, stylisRTLPlugin],
});
const cacheProviderRtl = (props) => {
return <CacheProvider value={cache}>{props.children}</CacheProvider>;
}
};
export default cacheProviderRtl
export default cacheProviderRtl;

View File

@@ -0,0 +1,60 @@
"use client";
import { toast } from "react-toastify";
import {
errorClientToast,
errorLogicToast,
errorServerToast,
errorTooManyToast,
errorUnauthorizedToast,
errorValidationToast,
} from "@/core/components/toasts/error";
const isServerError = (status) => status >= 500 && status <= 599;
const isClientError = (status) => status >= 400 && status <= 499;
const errorServer = (response, notification, toastContainer) => {
if (notification) errorServerToast(toastContainer);
};
const errorClient = (response, notification, toastContainer) => {
switch (response.status) {
case 401:
if (notification) errorUnauthorizedToast(toastContainer);
break;
case 422:
if ("type" in response.data) {
if (Array.isArray(response.data.message)) {
response.data.message.map((item) => {
if (notification) errorLogicToast(item, toastContainer);
});
} else {
if (notification) errorLogicToast(response.data.message, toastContainer);
}
break;
}
if (notification) {
const errorsMap = Object.keys(response.data.errors);
const errorsArray = response.data.errors;
errorsMap.map((item, index) => {
errorValidationToast(errorsArray[item][0], toastContainer);
});
}
break;
case 429:
if (notification) errorTooManyToast(toastContainer);
break;
default:
if (notification) errorClientToast(toastContainer);
break;
}
};
export const errorResponse = (response, notification, toastContainer) => {
if (notification) toast.dismiss({ container: toastContainer });
if (isServerError(response.status)) {
errorServer(response, notification, toastContainer);
} else if (isClientError(response.status)) {
errorClient(response, notification, toastContainer);
}
};

View File

@@ -0,0 +1,17 @@
export function filterMenuItems(items, _permissions = []) {
return items.reduce((acc, item) => {
if (item.permissions) {
if (item.permissions.some(permission => _permissions.includes(permission))) {
acc.push(item);
}
} else if (item.hasSubitems) {
const filteredSubItems = filterMenuItems(item.Subitems, _permissions);
if (filteredSubItems.length > 0) {
acc.push({ ...item, Subitems: filteredSubItems });
}
} else {
acc.push(item);
}
return acc;
}, []);
}

View File

@@ -7,4 +7,4 @@ export const flattenArrayOfObjects = (array, key) => {
}
return acc;
}, []);
};
};

378
src/core/utils/pageMenu.js Normal file
View File

@@ -0,0 +1,378 @@
import { AirlineSeatReclineNormal, Assessment, Repartition, Security, SpaceDashboard } from "@mui/icons-material";
export const pageMenu = [
{
id: "dashboard",
label: "پیشخوان",
type: "page",
route: "/dashboard",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["all"]
},
{
id: "userManagement",
label: "مدیریت کاربران",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/user_management",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['full-user-management', 'limited-user-management']
},
{
id: "projectsManagment",
label: "پروژه های راهداری",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "projectsManagmentFinance",
label: "ثبت قرارداد و پروژه",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/rahdari_projects/finance",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-contract', 'show-contract-province'],
},
{
id: "projectsManagmentList",
label: "لیست پروژه ها",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/rahdari_projects/projects_list",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["all"]
},
{
id: "projectsManagmentProposal",
label: "پروژه های پیشنهادی",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/rahdari_projects/proposal",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-proposal', 'show-proposal-province']
},
{
id: "projectsManagmentLawmaker",
label: "درخواست نمایندگان",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/rahdari_projects/lawmakers",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-lawmaker', 'show-lawmaker-province']
},
{
id: "projectsManagmentMap",
label: "پراکندگی بر روی نقشه",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=rahdari_projects",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["all"]
},
],
},
{
id: "roadItemManagment",
label: "فعالیت های روزانه",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "roadItemManagmentSupervisor",
label: "ارزیابی",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "roadItemManagmentSupervisorCartable",
label: "کارتابل",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/supervisor/cartable",
permissions: ['show-road-item-supervise-cartable', 'show-road-item-supervise-cartable-province']
},
],
},
{
id: "roadItemManagmentOparation",
label: "عملیات",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "roadItemManagmentOparationCreate",
label: "ثبت فعالیت",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/operator/create",
permissions: ['create-road-item']
},
{
id: "roadItemManagmentOparationCartable",
label: "کارتابل",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/operator/cartable",
permissions: ['create-road-item']
},
],
},
{
id: "roadItemManagmentMap",
label: "پراکندگی بر روی نقشه",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=road_items",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-road-item-supervise-cartable', 'show-road-item-supervise-cartable-province', 'create-road-item']
},
{
id: "roadItemManagmentReport",
label: "گزارش ها",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_items/report",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-road-item-supervise-cartable', 'show-road-item-supervise-cartable-province', 'create-road-item']
},
],
},
{
id: "roadPatrolManagment",
label: "گشت راهداری و ترابری",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "roadPatrolManagmentSupervisor",
label: "ارزیابی",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "roadPatrolManagmentSupervisorCartable",
label: "کارتابل",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/road_patrols/supervisor/cartable",
permissions: ['show-road-patrol-supervise-cartable', 'show-road-patrol-supervise-cartable-province']
},
],
},
{
id: "roadPatrolManagmentOparation",
label: "عملیات",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "roadPatrolManagmentOparationCreate",
label: "ثبت اقدام",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_patrols/operator/create",
permissions: ['add-road-patrol']
},
{
id: "roadPatrolManagmentOparationCartable",
label: "کارتابل",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_patrols/operator/cartable",
permissions: ['add-road-patrol']
},
],
},
{
id: "roadPatrolManagmentMap",
label: "پراکندگی بر روی نقشه",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=road_patrols",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['add-road-patrol', 'show-road-patrol-supervise-cartable', 'show-road-patrol-supervise-cartable-province']
},
{
id: "roadPatrolManagmentReport",
label: "گزارش ها",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_patrols/report",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['add-road-patrol', 'show-road-patrol-supervise-cartable', 'show-road-patrol-supervise-cartable-province']
},
],
},
{
id: "safetyAndPrivacyManagment",
label: "نگهداری حریم راه",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "safetyAndPrivacyManagmentOparation",
label: "عملیات",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "safetyAndPrivacyManagmentOparationCreate",
label: "ثبت فعالیت",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/safety_and_privacy/operator/first_step",
permissions: ['add-safety-and-privacy']
},
{
id: "safetyAndPrivacyManagmentOparationCartable",
label: "کارتابل",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/safety_and_privacy/operator/cartable",
permissions: ['show-safety-and-privacy-operator-cartable', 'show-safety-and-privacy-operator-cartable-province', 'show-safety-and-privacy-operator-cartable-edarate-shahri']
},
],
},
{
id: "safetyAndPrivacyManagmentMap",
label: "پراکندگی بر روی نقشه",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=safety_and_privacy",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-safety-and-privacy-operator-cartable', 'show-safety-and-privacy-operator-cartable-province',
'show-safety-and-privacy-operator-cartable-edarate-shahri', 'add-safety-and-privacy']
},
{
id: "safetyAndPrivacyManagmentReport",
label: "گزارش ها",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/safety_and_privacy/report",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-safety-and-privacy-operator-cartable', 'show-safety-and-privacy-operator-cartable-province',
'show-safety-and-privacy-operator-cartable-edarate-shahri', 'add-safety-and-privacy']
},
],
},
{
id: "roadObservationsManagment",
label: "واکنش سریع",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "roadObservationsManagmentSupervisor",
label: "ارزیابی",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "roadObservationsManagmentSupervisorCartable",
label: "کارتابل",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_observations/supervisor/cartable",
permissions: ['supervise-fast-react', 'supervise-fast-react-province']
},
],
},
{
id: "roadObservationsManagmentList",
label: "رسیدگی به شکایات",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_observations",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-fast-react', 'show-fast-react-province', 'show-fast-react-edarate-shahri']
},
{
id: "roadObservationsManagmentOparation",
label: "عملیات",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "roadObservationsManagmentOparationCartable",
label: "کارتابل",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_observations/operator/cartable",
permissions: ['show-fast-react', 'show-fast-react-province', 'show-fast-react-edarate-shahri']
},
],
},
{
id: "roadObservationsManagmentMap",
label: "پراکندگی بر روی نقشه",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=road_observations",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["all"]
},
{
id: "roadObservationsManagmentReport",
label: "گزارش ها",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/road_observations/report/index",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["all"]
},
],
},
{
id: "winterCampManagment",
label: "قرارگاه زمستانی",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "winterCampManagmentBlocking",
label: "محور های انسدادی",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/winter_camp",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-camp', 'show-camp-province']
},
{
id: "winterCampManagmentFirstRing",
label: "محور های حلقه اول",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=camp",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-camp', 'show-camp-province']
},
{
id: "winterCampManagmentReport",
label: "گزارش ها",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/map?type=campNew",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ['show-camp', 'show-camp-province']
},
],
},
{
id: "receiptManagment",
label: "خسارات وارده بر ابنیه فنی و تاسیسات راه",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "receiptManagmentOparation",
label: "عملیات",
type: "menu",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
hasSubitems: true,
Subitems: [
{
id: "receiptManagmentOparationCartable",
label: "کارتابل",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/receipt",
permissions: ["all"]
},
],
},
{
id: "receiptManagmentReport",
label: "گزارش ها",
type: "page",
route: process.env.NEXT_PUBLIC_API_URL + "/v2/receipt/report",
icon: <Security sx={{ width: "inherit", height: "inherit" }} />,
permissions: ["all"]
},
],
},
];

6
src/core/utils/routes.js Normal file
View File

@@ -0,0 +1,6 @@
const api = process.env.NEXT_PUBLIC_API_URL;
export const GET_USER_ROUTE = api + "/webapi/user/get-permission";
export const GET_LOGIN_ROUTE = api + "/login_dev";
export const GET_PERMISSIONS_ROUTE = api + "/webapi/user/get-permission";

View File

@@ -0,0 +1,10 @@
"use client";
import { toast } from "react-toastify";
import { successToast } from "@/core/components/toasts/success";
export const successRequest = (notification, toastContainer) => {
if (notification) {
toast.dismiss({ container: toastContainer });
successToast(toastContainer);
}
};

View File

@@ -1,12 +1,22 @@
'use client'
"use client";
import { createTheme } from "@mui/material";
const theme = createTheme({
direction: 'rtl',
direction: "rtl",
typography: {
fontFamily: `IRANSansFaNum, sans-serif`,
fontSize: 12
fontSize: 12,
},
palette: {
primary: {
main: "#2070af",
contrastText: "#fff",
},
primary2: {
main: "#015688",
contrastText: "#fff",
},
},
});
export default theme
export default theme;

View File

@@ -1,162 +1,120 @@
import { alpha, darken } from "@mui/material";
export const parseCSSVarId = (id) => id.replace(/[^a-zA-Z0-9]/g, '_');
export const parseCSSVarId = (id) => id.replace(/[^a-zA-Z0-9]/g, "_");
export const parseFromValuesOrFunc = (
fn,
arg
) => (fn instanceof Function ? fn(arg) : fn);
export const parseFromValuesOrFunc = (fn, arg) => (fn instanceof Function ? fn(arg) : fn);
export const getCommonToolbarStyles = ({
table,
}) => ({
alignItems: 'flex-start',
backgroundColor: table.options.mrtTheme.baseBackgroundColor,
display: 'grid',
flexWrap: 'wrap-reverse',
minHeight: '3.5rem',
overflow: 'hidden',
position: 'relative',
transition: 'all 150ms ease-in-out',
zIndex: 1,
export const getCommonToolbarStyles = ({ table }) => ({
alignItems: "flex-start",
backgroundColor: table.options.mrtTheme.baseBackgroundColor,
display: "grid",
flexWrap: "wrap-reverse",
minHeight: "3.5rem",
overflow: "hidden",
position: "relative",
transition: "all 150ms ease-in-out",
zIndex: 1,
});
export const getCommonTooltipProps = (
placement,
) => ({
disableInteractive: true,
enterDelay: 500,
enterNextDelay: 500,
placement,
export const getCommonTooltipProps = (placement) => ({
disableInteractive: true,
enterDelay: 500,
enterNextDelay: 500,
placement,
});
export const flipIconStyles = (theme) =>
theme.direction === 'rtl'
? { style: { transform: 'scaleX(-1)' } }
: undefined;
theme.direction === "rtl" ? { style: { transform: "scaleX(-1)" } } : undefined;
export const getCommonPinnedCellStyles = ({
column,
table,
theme,
}) => {
const { baseBackgroundColor } = table.options.mrtTheme;
const isPinned = column?.getIsPinned();
export const getCommonPinnedCellStyles = ({ column, table, theme }) => {
const { baseBackgroundColor } = table.options.mrtTheme;
const isPinned = column?.getIsPinned();
return {
'&[data-pinned="true"]': {
'&:before': {
backgroundColor: alpha(
darken(
baseBackgroundColor,
theme.palette.mode === 'dark' ? 0.05 : 0.01,
),
0.97,
),
boxShadow: column
? isPinned === 'left' && column.getIsLastColumn(isPinned)
? `-4px 0 4px -4px ${alpha(theme.palette.grey[700], 0.5)} inset`
: isPinned === 'right' && column.getIsFirstColumn(isPinned)
? `4px 0 4px -4px ${alpha(theme.palette.grey[700], 0.5)} inset`
: undefined
: undefined,
...commonCellBeforeAfterStyles,
},
},
};
return {
'&[data-pinned="true"]': {
"&:before": {
backgroundColor: alpha(darken(baseBackgroundColor, theme.palette.mode === "dark" ? 0.05 : 0.01), 0.97),
boxShadow: column
? isPinned === "left" && column.getIsLastColumn(isPinned)
? `-4px 0 4px -4px ${alpha(theme.palette.grey[700], 0.5)} inset`
: isPinned === "right" && column.getIsFirstColumn(isPinned)
? `4px 0 4px -4px ${alpha(theme.palette.grey[700], 0.5)} inset`
: undefined
: undefined,
...commonCellBeforeAfterStyles,
},
},
};
};
export const getCommonMRTCellStyles = ({
column,
header,
table,
tableCellProps,
theme,
}) => {
const {
getState,
options: { enableColumnVirtualization, layoutMode },
} = table;
const { draggingColumn } = getState();
const { columnDef } = column;
const { columnDefType } = columnDef;
export const getCommonMRTCellStyles = ({ column, header, table, tableCellProps, theme }) => {
const {
getState,
options: { enableColumnVirtualization, layoutMode },
} = table;
const { draggingColumn } = getState();
const { columnDef } = column;
const { columnDefType } = columnDef;
const isColumnPinned =
columnDef.columnDefType !== 'group' && column.getIsPinned();
const isColumnPinned = columnDef.columnDefType !== "group" && column.getIsPinned();
const widthStyles = {
minWidth: `max(calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId(
header?.id ?? column.id,
)}-size) * 1px), ${columnDef.minSize ?? 30}px)`,
width: `calc(var(--${header ? 'header' : 'col'}-${parseCSSVarId(
header?.id ?? column.id,
)}-size) * 1px)`,
};
const widthStyles = {
minWidth: `max(calc(var(--${header ? "header" : "col"}-${parseCSSVarId(
header?.id ?? column.id
)}-size) * 1px), ${columnDef.minSize ?? 30}px)`,
width: `calc(var(--${header ? "header" : "col"}-${parseCSSVarId(header?.id ?? column.id)}-size) * 1px)`,
};
if (layoutMode === 'grid') {
widthStyles.flex = `${[0, false].includes(columnDef.grow)
? 0
: `var(--${header ? 'header' : 'col'}-${parseCSSVarId(
header?.id ?? column.id,
)}-size)`
} 0 auto`;
} else if (layoutMode === 'grid-no-grow') {
widthStyles.flex = `${+(columnDef.grow || 0)} 0 auto`;
}
const pinnedStyles = isColumnPinned
? {
...getCommonPinnedCellStyles({ column, table, theme }),
left:
isColumnPinned === 'left'
? `${column.getStart('left')}px`
: undefined,
opacity: 0.97,
position: 'sticky',
right:
isColumnPinned === 'right'
? `${column.getAfter('right')}px`
: undefined,
if (layoutMode === "grid") {
widthStyles.flex = `${
[0, false].includes(columnDef.grow)
? 0
: `var(--${header ? "header" : "col"}-${parseCSSVarId(header?.id ?? column.id)}-size)`
} 0 auto`;
} else if (layoutMode === "grid-no-grow") {
widthStyles.flex = `${+(columnDef.grow || 0)} 0 auto`;
}
: {};
return {
backgroundColor: 'inherit',
backgroundImage: 'inherit',
display: layoutMode?.startsWith('grid') ? 'flex' : undefined,
justifyContent:
columnDefType === 'group'
? 'center'
: layoutMode?.startsWith('grid')
? tableCellProps.align
: undefined,
opacity:
table.getState().draggingColumn?.id === column.id ||
table.getState().hoveredColumn?.id === column.id
? 0.5
: 1,
position: 'relative',
transition: enableColumnVirtualization
? 'none'
: `padding 150ms ease-in-out`,
zIndex:
column.getIsResizing() || draggingColumn?.id === column.id
? 2
: columnDefType !== 'group' && isColumnPinned
? 1
: 0,
...pinnedStyles,
...widthStyles,
...(parseFromValuesOrFunc(tableCellProps?.sx, theme)),
};
const pinnedStyles = isColumnPinned
? {
...getCommonPinnedCellStyles({ column, table, theme }),
left: isColumnPinned === "left" ? `${column.getStart("left")}px` : undefined,
opacity: 0.97,
position: "sticky",
right: isColumnPinned === "right" ? `${column.getAfter("right")}px` : undefined,
}
: {};
return {
backgroundColor: "inherit",
backgroundImage: "inherit",
display: layoutMode?.startsWith("grid") ? "flex" : undefined,
justifyContent:
columnDefType === "group" ? "center" : layoutMode?.startsWith("grid") ? tableCellProps.align : undefined,
opacity:
table.getState().draggingColumn?.id === column.id || table.getState().hoveredColumn?.id === column.id
? 0.5
: 1,
position: "relative",
transition: enableColumnVirtualization ? "none" : `padding 150ms ease-in-out`,
zIndex:
column.getIsResizing() || draggingColumn?.id === column.id
? 2
: columnDefType !== "group" && isColumnPinned
? 1
: 0,
...pinnedStyles,
...widthStyles,
...parseFromValuesOrFunc(tableCellProps?.sx, theme),
};
};
export const commonCellBeforeAfterStyles = {
content: '""',
height: '100%',
left: 0,
position: 'absolute',
top: 0,
width: '100%',
zIndex: -1,
};
content: '""',
height: "100%",
left: 0,
position: "absolute",
top: 0,
width: "100%",
zIndex: -1,
};

View File

@@ -0,0 +1,11 @@
import { createContext, useState } from "react";
export const DataTableContext = createContext();
const DataTableProvider = ({ children }) => {
const [filterData, setFilterData] = useState({});
return <DataTableContext.Provider value={{ filterData, setFilterData }}>{children}</DataTableContext.Provider>;
};
export default DataTableProvider;

82
src/lib/contexts/auth.js Normal file
View File

@@ -0,0 +1,82 @@
"use client";
import { createContext, useCallback, useContext, useEffect, useReducer } from "react";
import useRequest from "../hooks/useRequest";
import { GET_USER_ROUTE } from "@/core/utils/routes";
const initAuth = {
initAuthState: false,
isAuth: false,
user: {},
};
const authReducer = (state, action) => {
switch (action.type) {
case "CLEAR_USER":
return { ...state, user: {} };
case "CHANGE_USER":
return { ...state, user: action.user };
case "CHANGE_AUTH_STATE":
return { ...state, isAuth: action.isAuth };
case "CHANGE_INIT_AUTH":
return { ...state, initAuthState: action.initAuthState };
default:
return state;
}
};
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, initAuth);
const request = useRequest();
const clearUser = useCallback(() => {
dispatch({ type: "CLEAR_USER" });
}, []);
const changeUser = useCallback((user) => {
dispatch({ type: "CHANGE_USER", user });
}, []);
const changeAuthState = useCallback((isAuth) => {
dispatch({ type: "CHANGE_AUTH_STATE", isAuth });
}, []);
const changeInitAuth = useCallback((initAuthState) => {
dispatch({ type: "CHANGE_INIT_AUTH", initAuthState });
}, []);
const getUser = useCallback(async () => {
try {
const { data } = await request(GET_USER_ROUTE, "get");
changeUser(data);
changeAuthState(true);
changeInitAuth(true);
} catch (error) {
if (error.response && error.response.status === 401) {
clearUser();
changeAuthState(false);
changeInitAuth(true);
}
}
}, [clearUser, changeUser, changeAuthState, changeInitAuth]);
useEffect(() => {
getUser();
}, []);
return (
<AuthContext.Provider
value={{
user: state.user,
isAuth: state.isAuth,
initAuthState: state.initAuthState,
getUser,
}}
>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);

View File

@@ -1,27 +1,26 @@
'use client'
import {createContext, useCallback, useEffect, useState} from "react";
import {flattenArrayOfObjects} from "@/core/utils/flattenArrayOfObjects";
"use client";
import { createContext, useCallback, useEffect, useState } from "react";
export const TableSettingContext = createContext();
export const TableSettingProvider = ({children}) => {
export const TableSettingProvider = ({ children }) => {
const [settingStore, setSettingStore] = useState({});
useEffect(() => {
const _localStorage = localStorage.getItem("_setting-app")
const _localStorage = localStorage.getItem("_setting-app");
if (_localStorage) {
setSettingStore(JSON.parse(_localStorage))
setSettingStore(JSON.parse(_localStorage));
}
}, []);
const hideAction = useCallback((user_id, page_name, table_name, settingValue, columns) => {
updateSettingStorage(user_id, page_name, table_name, "hides", settingValue, columns);
}, [])
}, []);
const sortAction = useCallback((user_id, page_name, table_name, settingValue, columns) => {
updateSettingStorage(user_id, page_name, table_name, "sorts", settingValue, columns);
}, [])
}, []);
const filterAction = (user_id, page_name, table_name, settingValue, columns) => {
updateSettingStorage(user_id, page_name, table_name, "filters", settingValue, columns);
}
};
const updateSettingStorage = (user_id, page_name, table_name, settingType, settingValue, columns) => {
const prevLocalStorage = JSON.parse(localStorage.getItem("_setting-app")) || {};
const userSettings = prevLocalStorage[user_id] || {};
@@ -31,7 +30,7 @@ export const TableSettingProvider = ({children}) => {
if (settingType === "sorts" || settingType === "filters") {
newSettings = [...settingValue];
} else {
newSettings = {...tableSettings[settingType] || {}, ...settingValue};
newSettings = { ...(tableSettings[settingType] || {}), ...settingValue };
}
const updatedLocalStorage = {
...prevLocalStorage,
@@ -41,19 +40,38 @@ export const TableSettingProvider = ({children}) => {
...pageSettings,
[table_name]: {
...tableSettings,
[settingType]: Array.isArray(settingValue) ? [...newSettings] : {...newSettings},
summary: SummaryTextMessage({
...tableSettings,
[settingType]: Array.isArray(settingValue) ? [...newSettings] : {...newSettings},
}, columns)
[settingType]: Array.isArray(settingValue) ? [...newSettings] : { ...newSettings },
summary: SummaryTextMessage(
{
...tableSettings,
[settingType]: Array.isArray(settingValue) ? [...newSettings] : { ...newSettings },
},
columns
),
},
},
},
};
localStorage.setItem("_setting-app", JSON.stringify(updatedLocalStorage));
setSettingStore(updatedLocalStorage);
}
};
const refactorAction = (user_id, page_name, table_name) => {
const prevLocalStorage = JSON.parse(localStorage.getItem("_setting-app")) || {};
const userSettings = prevLocalStorage[user_id] || {};
const pageSettings = userSettings[page_name] || {};
const refactorData = {
...prevLocalStorage,
[user_id]: {
...userSettings,
[page_name]: {
...pageSettings,
[table_name]: {},
},
},
};
localStorage.setItem("_setting-app", JSON.stringify(refactorData));
setSettingStore(refactorData);
};
const SummaryTextMessage = (values, columns) => {
let SummaryText = "";
let HideText = "";
@@ -61,38 +79,35 @@ export const TableSettingProvider = ({children}) => {
let SortText = "";
if (values.sorts && values.sorts.length > 0) {
const _list = []
const _list = [];
for (const sort of values.sorts) {
const _column = columns.find(column => column.id == sort.id)
if (_column)
_list.push(`${_column.header}` + `(${sort.desc ? "نزولی" : "صعودی"})`)
const _column = columns.find((column) => column.id == sort.id);
if (_column) _list.push(`${_column.header}` + `(${sort.desc ? "نزولی" : "صعودی"})`);
}
if (_list.length > 0)
SortText = " مرتب شده بر اساس : " + _list.join(", ")
if (_list.length > 0) SortText = " مرتب شده بر اساس : " + _list.join(", ");
}
if (values.filters && values.filters.length > 0) {
const _list = []
const _list = [];
for (const filter of values.filters) {
const _column = columns.find(column => column.id == filter.id)
if (_column)
_list.push(`${_column.header}`)
const _column = columns.find((column) => column.id == filter.id);
if (_column) _list.push(`${_column.header}`);
}
if (_list.length > 0)
FilterText = " فیلتر شده بر اساس : " + _list.join(", ")
if (_list.length > 0) FilterText = " فیلتر شده بر اساس : " + _list.join(", ");
}
if (values.hides) {
const hidesArray = Object.entries(values.hides).map((e) => ({id: e[0], value: e[1]}))
const hidesArray = Object.entries(values.hides).map((e) => ({
id: e[0],
value: e[1],
}));
if (hidesArray.length > 0) {
const _list = []
const _list = [];
for (const hide of hidesArray) {
if (hide.value) continue
const _column = columns.find(c => c.id == hide.id)
if (_column)
_list.push(_column.header)
if (hide.value) continue;
const _column = columns.find((c) => c.id == hide.id);
if (_column) _list.push(_column.header);
}
if (_list.length > 0)
HideText = "ستون های مخفی شده : " + _list.join(", ");
if (_list.length > 0) HideText = "ستون های مخفی شده : " + _list.join(", ");
}
}
@@ -113,7 +128,7 @@ export const TableSettingProvider = ({children}) => {
}
}
if (FilterText) {
SummaryText += FilterText
SummaryText += FilterText;
}
}
@@ -122,8 +137,15 @@ export const TableSettingProvider = ({children}) => {
return (
<TableSettingContext.Provider
value={{settingStore, hideAction, sortAction, filterAction}}>
value={{
settingStore,
hideAction,
sortAction,
filterAction,
refactorAction,
}}
>
{children}
</TableSettingContext.Provider>
);
};
};

View File

@@ -0,0 +1,9 @@
import { useContext } from "react";
import { DataTableContext } from "@/lib/contexts/DataTable";
const useTableSetting = () => {
const { filterData, setFilterData } = useContext(DataTableContext);
return { filterData, setFilterData };
};
export default useTableSetting;

View File

@@ -0,0 +1,18 @@
import { GET_PERMISSIONS_ROUTE } from "@/core/utils/routes";
import useSWR from "swr";
import useRequest from "./useRequest";
export const usePermissions = () => {
const request = useRequest()
const fetcher = async (url) => {
try {
const response = await request(url)
return response.data.data
} catch (error) {
throw new Error();
}
};
return useSWR(GET_PERMISSIONS_ROUTE, fetcher, { keepPreviousData: true });
}

Some files were not shown because too many files have changed in this diff Show More