Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade rules page #704

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

upgrade rules page #704

wants to merge 1 commit into from

Conversation

ninedev-i
Copy link
Contributor

@ninedev-i ninedev-i commented Feb 21, 2025

Обновил страницы правил и редактирования правил
image

Summary by CodeRabbit

  • New Features

    • Enhanced rule management now lets users include, copy, and rename configurations.
    • Updated interface messages in English and Russian for rule actions and page errors.
  • Refactor

    • Streamlined navigation by removing legacy script editor features and introducing dedicated rule editor views.
    • Upgraded component routing and structure for improved user interactions.
  • Style

    • Refined button designs (with icon support and additional variants), input layouts, and overall page styling with new responsive CSS variables.

@ninedev-i ninedev-i requested a review from KraPete February 21, 2025 14:49
@ninedev-i ninedev-i requested a review from a team as a code owner February 21, 2025 14:49
Copy link

coderabbitai bot commented Feb 21, 2025

Walkthrough

This pull request updates the application by releasing version 2.111.0 of the package with new rule management features, including the ability to include, copy, and rename rules. It removes several script editor components and controllers while migrating AngularJS states to a component-based approach. New React directives and components for rule editing and code editing using CodeMirror are added. Localization for rules has been enhanced in both English and Russian, and various style, ESLint, and utility updates have been introduced to support the changes.

Changes

File(s) Change Summary
debian/changelog New version entry for wb-mqtt-homeui (2.111.0) with rule inclusion, copy, and rename capabilities.
frontend/app/scripts/app.js Removed import of script-editor-page.css and elimination of the 'rules' module reference.
frontend/app/scripts/app.routes.js Updated routes: removed controllers, changed templates to <rules-page> and <rule-page>, and updated import statements for rules states.
frontend/app/scripts/controllers/ruleController.js,
.../rulesController.js
New modules (homeuiApp.rule) with rulePage and rulesPage directives added.
frontend/app/scripts/controllers/scriptController.js,
.../scriptsController.js
Removed AngularJS controllers and modules related to script management.
frontend/app/scripts/i18n/react/locales/en/translations.json,
.../ru/translations.json
Added new "rules" sections and updated page error messages.
frontend/app/scripts/i18n/rules/en.json,
.../ru.json
Removed legacy internationalization files for rules.
frontend/app/scripts/react-directives/rule/*.tsx,
.../rules/*.tsx
Added new React directives (ruleDirective and rulesDirective) to integrate rule components in AngularJS.
frontend/app/scripts/react-directives/script-editor/* Removed script editor directives and related React components/files.
frontend/app/styles/css/script-editor-page.css
frontend/app/views/script.html
frontend/app/views/scripts.html
Removed references and components related to the old script editor interface.
frontend/eslint.config.mjs Added two new JSX formatting rules for prop layout.
frontend/src/assets/styles/variables.css Introduced new CSS variables --warn-color and --table-item-hover.
frontend/src/components/button/* Updated Button component to support an optional icon prop and added a new 'success' variant with styling updates.
frontend/src/components/cell/* Added optional prop isWithExplicitChanges to Input component in cell components.
frontend/src/components/code-editor/* New CodeEditor component and supporting files (helpers, types, styles) introduced with CodeMirror integration.
frontend/src/components/input/* Enhanced Input component with new props isFullWidth and isWithExplicitChanges and corresponding style updates.
frontend/src/components/switch/switch.tsx Added default onChange value and stop propagation on label clicks.
frontend/src/layouts/page/* Updated PageLayout to support title editing, sticky header, error alerts, and additional state handling.
frontend/src/pages/rules/[rule]/* and
frontend/src/pages/rules/index/*
New pages and styles for rule editing and listing integrated with the new rules store.
frontend/src/stores/rules/* New RulesStore class and related types for managing rules using MobX.
frontend/src/utils/id.ts,
frontend/src/utils/url.ts
Added utility functions: generateNextId for ID generation and getPathname for URL pathname extraction.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant ERP as EditRulePage
    participant RS  as RulesStore
    participant CE  as CodeEditor
    participant NT  as Notification System

    U->>ERP: Open rule editing page
    ERP->>RS: Request rule load (using current URL)
    RS-->>ERP: Return rule content/errors
    ERP->>CE: Render CodeEditor with rule content
    U->>ERP: Modify rule via CodeEditor
    U->>ERP: Click Save
    ERP->>RS: Invoke save(rule)
    RS-->>ERP: Return save status
    ERP->>NT: Display notification based on result
    NT-->>U: Show success/error message
Loading

Suggested reviewers

  • KraPete
  • lostpoint-ru

Poem

Oh, how my paws do dance with glee,
A new rule blooms in our app’s spree!
With changes vast from code to style,
I hop along each extra mile.
From backups to routing, and CSS that sings,
Celebrate our progress—let joy take wings!
🐰🌟 Happy coding, friends!

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 30

🔭 Outside diff range comments (3)
frontend/src/components/cell/cell-value.tsx (1)

96-96: 🛠️ Refactor suggestion

Consider safer alternatives to dangerouslySetInnerHTML.

The use of dangerouslySetInnerHTML for space formatting could be replaced with a safer CSS-based solution.

Here's a suggested implementation using CSS:

-<span dangerouslySetInnerHTML={{ __html: formattedValue }}></span>
+<span className="deviceCell-value-formatted">
+  {formattedValue.split(' ').map((part, index) => (
+    <React.Fragment key={index}>
+      {index > 0 && <span className="deviceCell-space" />}
+      {part}
+    </React.Fragment>
+  ))}
+</span>

/* Add to CSS */
+.deviceCell-space {
+  display: inline-block;
+  width: 0.25em;
+}
🧰 Tools
🪛 Biome (1.9.4)

[error] 96-96: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)


[error] 96-96: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

frontend/src/utils/url.ts (1)

1-5: 🧹 Nitpick (assertive)

Handle empty or malformed hashes gracefully.

If url.hash is empty or missing slashes, .pop() may return undefined. While decodeURIComponent('') is not harmful, consider a fallback:

-return decodeURIComponent(url.hash.split('/').pop());
+return decodeURIComponent(url.hash.split('/').pop() ?? '');

This ensures a safe default return value.

frontend/app/scripts/react-directives/rule/rule.tsx (1)

1-30: 🛠️ Refactor suggestion

Consider extracting shared directive logic.

This file contains significant code duplication with rules.tsx. Consider creating a shared higher-order directive or utility function.

// shared-directive.ts
export const createReactDirective = (Component, options = {}) => {
  return (whenMqttReady, EditorProxy, rolesFactory) => {
    'ngInject';
    
    setReactLocale();
    
    return {
      restrict: 'E',
      scope: {
        path: '=',
        isNew: '=',
        ...options.scope,
      },
      link: function(scope, element) {
        const hasRights = rolesFactory.checkRights(rolesFactory.ROLE_THREE);
        scope.store = new RulesStore(whenMqttReady, EditorProxy);
        scope.root = ReactDOM.createRoot(element[0]);
        scope.root.render(<Component rulesStore={scope.store} hasRights={hasRights} />);
        
        element.on('$destroy', () => {
          scope.store.dispose && scope.store.dispose();
          scope.root.unmount();
        });
      },
      ...options.directiveOptions,
    };
  };
};

// Usage in rule.tsx and rules.tsx
export default createReactDirective(EditRulePage);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae3896c and cd6596c.

⛔ Files ignored due to path filters (4)
  • frontend/src/assets/icons/copy.svg is excluded by !**/*.svg
  • frontend/src/assets/icons/edit.svg is excluded by !**/*.svg
  • frontend/src/assets/icons/success.svg is excluded by !**/*.svg
  • frontend/src/assets/icons/warn.svg is excluded by !**/*.svg
📒 Files selected for processing (50)
  • debian/changelog (1 hunks)
  • frontend/app/scripts/app.js (0 hunks)
  • frontend/app/scripts/app.routes.js (3 hunks)
  • frontend/app/scripts/controllers/ruleController.js (1 hunks)
  • frontend/app/scripts/controllers/rulesController.js (1 hunks)
  • frontend/app/scripts/controllers/scriptController.js (0 hunks)
  • frontend/app/scripts/controllers/scriptsController.js (0 hunks)
  • frontend/app/scripts/i18n/react/locales/en/translations.json (1 hunks)
  • frontend/app/scripts/i18n/react/locales/ru/translations.json (1 hunks)
  • frontend/app/scripts/i18n/rules/en.json (0 hunks)
  • frontend/app/scripts/i18n/rules/ru.json (0 hunks)
  • frontend/app/scripts/react-directives/rule/rule.tsx (1 hunks)
  • frontend/app/scripts/react-directives/rules/rules.tsx (1 hunks)
  • frontend/app/scripts/react-directives/script-editor/pageStore.js (0 hunks)
  • frontend/app/scripts/react-directives/script-editor/script-editor.js (0 hunks)
  • frontend/app/scripts/react-directives/script-editor/scriptEditor.jsx (0 hunks)
  • frontend/app/scripts/react-directives/script-editor/scriptEditorPage.jsx (0 hunks)
  • frontend/app/styles/css/script-editor-page.css (0 hunks)
  • frontend/app/views/script.html (0 hunks)
  • frontend/app/views/scripts.html (0 hunks)
  • frontend/eslint.config.mjs (1 hunks)
  • frontend/src/assets/styles/variables.css (2 hunks)
  • frontend/src/components/button/button.tsx (1 hunks)
  • frontend/src/components/button/styles.css (3 hunks)
  • frontend/src/components/button/types.ts (1 hunks)
  • frontend/src/components/cell/cell-text.tsx (1 hunks)
  • frontend/src/components/cell/cell-value.tsx (1 hunks)
  • frontend/src/components/code-editor/code-editor.tsx (1 hunks)
  • frontend/src/components/code-editor/helpers.ts (1 hunks)
  • frontend/src/components/code-editor/index.ts (1 hunks)
  • frontend/src/components/code-editor/styles.css (1 hunks)
  • frontend/src/components/code-editor/types.ts (1 hunks)
  • frontend/src/components/input/input.tsx (2 hunks)
  • frontend/src/components/input/styles.css (1 hunks)
  • frontend/src/components/input/types.ts (1 hunks)
  • frontend/src/components/switch/switch.tsx (1 hunks)
  • frontend/src/layouts/page/page.tsx (1 hunks)
  • frontend/src/layouts/page/styles.css (2 hunks)
  • frontend/src/layouts/page/types.ts (1 hunks)
  • frontend/src/pages/rules/[rule]/edit-rule.tsx (1 hunks)
  • frontend/src/pages/rules/[rule]/index.ts (1 hunks)
  • frontend/src/pages/rules/[rule]/styles.css (1 hunks)
  • frontend/src/pages/rules/index/index.ts (1 hunks)
  • frontend/src/pages/rules/index/rules.tsx (1 hunks)
  • frontend/src/pages/rules/index/styles.css (1 hunks)
  • frontend/src/stores/rules/index.ts (1 hunks)
  • frontend/src/stores/rules/rules-store.ts (1 hunks)
  • frontend/src/stores/rules/types.ts (1 hunks)
  • frontend/src/utils/id.ts (1 hunks)
  • frontend/src/utils/url.ts (1 hunks)
💤 Files with no reviewable changes (12)
  • frontend/app/views/script.html
  • frontend/app/scripts/i18n/rules/en.json
  • frontend/app/styles/css/script-editor-page.css
  • frontend/app/scripts/app.js
  • frontend/app/scripts/i18n/rules/ru.json
  • frontend/app/views/scripts.html
  • frontend/app/scripts/react-directives/script-editor/scriptEditor.jsx
  • frontend/app/scripts/react-directives/script-editor/script-editor.js
  • frontend/app/scripts/controllers/scriptController.js
  • frontend/app/scripts/react-directives/script-editor/scriptEditorPage.jsx
  • frontend/app/scripts/react-directives/script-editor/pageStore.js
  • frontend/app/scripts/controllers/scriptsController.js
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/src/components/switch/switch.tsx

[error] 7-13: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)

frontend/src/utils/id.ts

[error] 9-9: Use Number.parseInt instead of the equivalent global.

ES2015 moved some globals into the Number namespace for consistency.
Safe fix: Use Number.parseInt instead.

(lint/style/useNumberNamespace)

frontend/src/pages/rules/[rule]/edit-rule.tsx

[error] 59-59: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.

(lint/style/useTemplate)


[error] 62-62: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.

(lint/style/useTemplate)

frontend/src/components/code-editor/helpers.ts

[error] 18-18: This let declares a variable that is only assigned once.

'e' is never reassigned.

Safe fix: Use const instead.

(lint/style/useConst)


[error] 35-35: This let declares a variable that is only assigned once.

'breakpoints' is never reassigned.

Safe fix: Use const instead.

(lint/style/useConst)

frontend/src/layouts/page/page.tsx

[error] 83-83: Provide screen reader accessible content when using a elements.

All links on a page should have content that is accessible to screen readers.
Accessible content refers to digital content that is designed and structured in a way that makes it easy for people with disabilities to access, understand, and interact with using assistive technologies.
Follow these links for more information,
WCAG 2.4.4
WCAG 4.1.2

(lint/a11y/useAnchorContent)


[error] 102-102: JSX elements without children should be marked as self-closing. In JSX, it is valid for any element to be self-closing.

Unsafe fix: Use a SelfClosingElement instead

(lint/style/useSelfClosingElements)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (51)
frontend/src/components/input/types.ts (1)

14-15: LGTM! Props are well-typed and follow React naming conventions.

The new optional props isFullWidth and isWithExplicitChanges are appropriately typed as booleans and follow React naming conventions.

frontend/src/components/cell/cell-text.tsx (1)

42-42: LGTM! Good use of explicit change handling.

The isWithExplicitChanges prop is appropriately used here to ensure controlled input behavior.

frontend/src/components/cell/cell-value.tsx (1)

69-69: LGTM! Good use of explicit change handling for numeric inputs.

The isWithExplicitChanges prop is appropriately used here to ensure controlled numeric input behavior.

frontend/src/components/input/styles.css (1)

21-25: Added full-width style for inputs.
The new .input-fullWidth class effectively sets the width to 100%, which should enhance the flexibility of input elements when the isFullWidth prop is enabled in the corresponding component. Ensure that the usage of this class is consistent across the application and aligns with your design system’s naming conventions.

frontend/src/components/code-editor/helpers.ts (2)

4-5: The breakpoint effect approach looks clean.

Defining a separate StateEffect keeps breakpoint toggling modular and composable. This clearly communicates the toggle intent within the editor state.


58-79: Validate error lines or provide fallback.

If an error line exceeds the total number of lines, it simply returns. This is safe. However, if .pop() inside line-based logic returns undefined, ensure it won't cause unintended side effects. Otherwise, the approach is solid.

frontend/src/layouts/page/page.tsx (5)

14-24: Prop expansions are flexible and descriptive.

Allowing errors, stickyHeader, and editing-related props provides robust customizability. Good job specifying defaults like errors = [] and isLoading = false.


25-32: Properly initializing state from props.

Using useEffect to sync titleValue with title and a separate state for isEditingTitleState is a nice pattern. This ensures you won't override user input unexpectedly.


33-44: 404 handling block is clear and user-friendly.

Displaying the not-found message with an alert is straightforward.


73-77: Meaningful error alerts.

Mapping over errors to display each alert by variant is concise. Confirm that the error.text is safe for display (e.g., sanitized, if user-input).


88-100: Conditional rendering of loader and content.

The stickyHeader usage is intuitive. Users can see a loader if isLoading is true; otherwise, the main content is displayed. This pattern is flexible and clean.

frontend/app/scripts/controllers/ruleController.js (1)

1-5:

✅ Verification successful

LGTM! Verify dependency availability.

The implementation correctly bridges AngularJS and React components. The directive is properly registered with necessary dependencies.

Run the following script to verify the dependency availability:


🏁 Script executed:

#!/bin/bash
# Description: Verify that all required dependencies are available.

# Check for MQTT service
rg -A 5 'whenMqttReady'

# Check for EditorProxy service
rg -A 5 'EditorProxy'

# Check for rolesFactory service
rg -A 5 'rolesFactory'

# Check for React directive
rg -A 5 'react-directives/rule/rule'

Length of output: 74307


Dependency Verification and Approval

The dependency checks confirm that all required services—whenMqttReady, EditorProxy, and rolesFactory—are properly declared and used throughout the codebase, and the React directive import from react-directives/rule/rule.tsx is consistent with the expected usage in the AngularJS module registration.

No further changes are needed.

frontend/src/components/button/types.ts (1)

5-7: LGTM! Enhanced button flexibility.

The changes improve the button component by:

  • Making the label optional to support icon-only buttons
  • Adding icon support for better visual representation
  • Including a success variant for positive actions
frontend/src/layouts/page/types.ts (1)

10-10: LGTM!

The property name is self-descriptive and the type is appropriate.

frontend/src/components/button/button.tsx (2)

6-6: LGTM! Props destructuring follows React best practices.

The props destructuring is clean and follows React best practices by providing default values for size and variant.


14-14: LGTM! Success variant class follows naming convention.

The success variant class follows the established naming pattern of other variants.

frontend/app/scripts/react-directives/rules/rules.tsx (1)

7-9: LGTM! Proper AngularJS directive setup with dependency injection.

The directive follows AngularJS best practices with proper dependency injection using 'ngInject'.

frontend/src/components/code-editor/code-editor.tsx (2)

12-25: LGTM! Proper error line handling and gutter effects.

The editor reinitialization logic properly handles error lines and gutter effects.


52-62: LGTM! Clean component rendering with proper props.

The CodeMirror component is rendered with all necessary props and proper style handling.

frontend/src/stores/rules/rules-store.ts (1)

23-40: LGTM! Well-structured rule loading logic.

The implementation properly handles asynchronous loading, error states, and MobX state updates.

frontend/src/pages/rules/[rule]/edit-rule.tsx (1)

30-45: LGTM! Well-structured initialization and error handling.

The component properly manages loading states and handles errors during rule loading.

frontend/eslint.config.mjs (1)

72-73: LGTM! Good addition of JSX formatting rules.

The new rules will help maintain consistent JSX formatting across the codebase.

frontend/app/scripts/app.routes.js (3)

171-182: Migration to component-based architecture for rules page.

The rules state has been updated to use a component-based approach with <rules-page /> instead of the previous template and controller pattern.


184-195: Migration to component-based architecture for rule editing.

The rules-edit state has been updated to use a component-based approach with <rule-page />. The chunk name has been changed from 'rules' to 'rule' to better reflect its purpose.


197-208: Migration to component-based architecture for new rule creation.

The rules-new state has been updated to use a component-based approach with <rule-page />, maintaining consistency with the rules-edit state.

debian/changelog (1)

1-5: LGTM! Clear and concise changelog entry.

The version bump to 2.111.0 is appropriate for the new feature addition. The changelog entry clearly describes the new functionality for rules management.

frontend/src/components/code-editor/index.ts (1)

1-2: LGTM! Clean barrel file implementation.

The file follows the standard pattern for barrel files, providing a clean export of the CodeEditor component.

frontend/src/pages/rules/index/index.ts (1)

1-4: LGTM! Clean barrel file implementation.

The file follows the standard pattern for barrel files, providing a clean default export of the RulesPage component.

frontend/src/pages/rules/[rule]/index.ts (1)

1-3: Simple and Clear Default Export

The file cleanly imports the EditRulePage component and re-exports it as the default export. This straightforward approach makes the module easy to consume and understand.

frontend/src/stores/rules/index.ts (1)

1-6: Centralizing the RulesStore Export

The file correctly imports the RulesStore from the rules-store module and re-exports it. This central index file improves module accessibility and maintainability.

frontend/src/components/code-editor/styles.css (1)

1-6: Appropriate Styling for the Code Editor Gutters

The new CSS rule for .codeEditor-gutters .cm-gutterElement is well-defined. The explicit settings (red color, padding, font size, and cursor) appear to directly address the UI enhancement goals for the code editor component.
Minor Note: Double-check that the red color aligns with the overall design and accessibility standards.

frontend/src/pages/rules/[rule]/styles.css (1)

1-14: Well-Structured Styles for the EditRulePage

The CSS rules for .editRule-container, .editRule-nameInput, and .editRule-error are clear and concise, providing the necessary stylistic guidelines for the rule editing interface.
Suggestion: Ensure that these styles integrate seamlessly with other page layouts, especially when used alongside global CSS variables or themes.

frontend/src/pages/rules/index/styles.css (1)

1-41: Comprehensive Styling for the Rules List Components

The CSS file provides a well-organized set of styles for the rules list items. The grid layout, hover/focus effects, and icon visibility settings demonstrate a thoughtful approach to interactive UI components.
Minor Suggestion: Verify that the usage of properties like fit-content(30px) is fully supported by your target browsers, or consider providing fallbacks if necessary. Also, ensure that all CSS variables (e.g., --border-color, --table-item-hover, --text-color, and --warn-color) are defined globally.

frontend/src/layouts/page/styles.css (6)

1-6: Good Implementation for the Page Container.
The new .page class sets the height and width to 100% and defines a flex column layout, which is appropriate for a full-page container.


13-21: Improved Header Container Spacing.
The modifications in .page-headerContainer—adjusting the margin on line 18 and adding a gap of 12px on line 20—enhance the spacing between header elements and align with modern flexbox layout practices.


23-29: Clear and Consistent Header Title Wrapper.
The newly introduced .page-headerTitleWrapper class effectively uses flexbox with a gap and fixed height. Ensure the fixed height (30px) meets all design requirements for varying content sizes, although it is acceptable if the design is fixed.


31-35: Icon for Editing Title is Appropriately Styled.
The .page-editTitleIcon class sets a proper width, inline-block display, and a pointer cursor, which is ideal for clickable icons.


41-48: Scrollable Container for Page Content.
The newly added .page-container class combines max-height with overflow management and a flex column layout. This design will support scrollable content within a flexible container effectively.


50-52: Spacing for Error Messages is Clear.
The .page-error class provides a bottom margin for error messages, ensuring that error blocks do not crowd other content.

frontend/src/components/button/styles.css (6)

8-10: Disabled State Visual Feedback.
The new rule for .button:disabled applies an opacity filter, which is a simple and effective method to indicate a disabled button.


28-30: Refined Hover State for Primary Buttons.
Updating the hover selector to .button-primary:not(:disabled):hover ensures that hover effects only occur for enabled buttons, thereby improving user experience.


38-40: Consistent Hover Feedback for Secondary Buttons.
The analogous change for .button-secondary:not(:disabled):hover aligns with the primary button adjustments and ensures consistent behavior across button variants.


42-44: Introduction of the Success Button Style.
The new .button-success class adds a success variant with its background set to var(--success-color). This provides an additional style option for actions that are contextually “successful.”


46-48: Hover State for Success Buttons.
The rule .button-success:not(:disabled):hover ensures that the hover state is applied correctly only when the button is active, using var(--success-color-hover).


57-67: Icon Button Enhancements.
The addition of .button-icon and its child rule for SVG elements (ensuring dimensions scale to 100%) are well implemented. This setup will help maintain consistent icon sizing within buttons.

frontend/src/assets/styles/variables.css (2)

12-12: Addition of Warning Color Variable.
Defining --warn-color: #eebc40; enriches the color palette for warning states. Verify that this value integrates well with the overall design language.


66-67: New Table Hover Variable Introduced.
Adding --table-item-hover: #fbfbfb; provides a subtle hover effect for table items, enhancing visual feedback in table interactions.

frontend/app/scripts/i18n/react/locales/en/translations.json (2)

438-462: Comprehensive Addition of the Rules Section.
The new "rules" section introduces all the necessary keys (title, buttons, labels, prompt, errors) for rule management. The structure is clear and consistent with the rest of the localization file.


463-467: Enhanced Page Not Found Messages.
The "page" section now includes "not-found" and "not-found-description" keys. The English messages are concise and inform users appropriately when a page is unavailable.

frontend/app/scripts/i18n/react/locales/ru/translations.json (2)

464-488: Добавление секции "rules" в русской локализации.
Новая секция "rules" включает все необходимые ключи (заголовок, кнопки, метки, подтверждения, ошибки) для управления правилами. Текст полон и соответствует содержанию английской версии.


490-493: Улучшенные сообщения для ошибок 404.
В секции "page" добавлены ключи "not-found" и "not-found-description", сообщающие пользователю о том, что запрошенная страница не существует. Тексты понятны и корректны.

return (
<input
type={type}
className={classNames('input', className, {
'input-m': size === 'default',
'input-s': size === 'small',
'input-fullWidth': isFullWidth,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider using CSS-in-JS or CSS modules for better encapsulation.

The class name input-fullWidth follows BEM naming convention, but consider using CSS-in-JS or CSS modules to avoid potential naming conflicts.

Comment on lines +39 to +44
const handleOnChange = (ev: ChangeEvent<HTMLInputElement>): void => {
setInternalValue(ev.target.value);
if (!isWithExplicitChanges) {
onChange(ev.target.value);
}
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider adding input validation and debouncing.

The new change handler logic looks good, but consider these improvements:

  1. Add input validation for numeric types.
  2. Consider debouncing frequent changes to prevent performance issues.

Here's a suggested implementation:

+import { useMemo } from 'react';
+import debounce from 'lodash/debounce';

 const handleOnChange = (ev: ChangeEvent<HTMLInputElement>): void => {
+  const newValue = ev.target.value;
+  
+  if (type === 'number' && !isValidNumber(newValue)) {
+    return;
+  }
+  
   setInternalValue(ev.target.value);
   if (!isWithExplicitChanges) {
-    onChange(ev.target.value);
+    debouncedOnChange(ev.target.value);
   }
 };

+const debouncedOnChange = useMemo(
+  () => debounce((value: string) => onChange(value), 300),
+  [onChange]
+);
+
+const isValidNumber = (value: string): boolean => {
+  const num = parseFloat(value);
+  return !isNaN(num) && 
+    (typeof min === 'undefined' || num >= min) && 
+    (typeof max === 'undefined' || num <= max);
+};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleOnChange = (ev: ChangeEvent<HTMLInputElement>): void => {
setInternalValue(ev.target.value);
if (!isWithExplicitChanges) {
onChange(ev.target.value);
}
};
import { useMemo } from 'react';
import debounce from 'lodash/debounce';
// ... other imports and component code
const handleOnChange = (ev: ChangeEvent<HTMLInputElement>): void => {
const newValue = ev.target.value;
if (type === 'number' && !isValidNumber(newValue)) {
return;
}
setInternalValue(ev.target.value);
if (!isWithExplicitChanges) {
debouncedOnChange(ev.target.value);
}
};
const debouncedOnChange = useMemo(
() => debounce((value: string) => onChange(value), 300),
[onChange]
);
const isValidNumber = (value: string): boolean => {
const num = parseFloat(value);
return (
!isNaN(num) &&
(typeof min === 'undefined' || num >= min) &&
(typeof max === 'undefined' || num <= max)
);
};
// ... rest of the component code

Comment on lines +1 to +3
import { EditorState, RangeSet, StateEffect, StateField } from '@codemirror/state';
import { EditorView, gutter, GutterMarker } from '@codemirror/view';

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider tree-shaking or partial imports.

If the bundle size is a concern, you could import only the required items from @codemirror/state and @codemirror/view to reduce final bundle size. Otherwise, this is a straightforward import statement.

Comment on lines +6 to +10
const breakpointMarker = new class extends GutterMarker {
toDOM() {
return document.createTextNode('⚠');
}
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Use an icon or distinct marker for breakpoints.

Currently, the marker is a text node '⚠'. Consider whether a custom icon or more descriptive marker would improve UX, particularly for visually impaired users or for color-blind friendly design.

Comment on lines +46 to +56
export const customGutter = gutter({
class: 'codeEditor-gutters',
markers: (view) => view.state.field(breakpointState) || RangeSet.empty,
domEventHandlers: {
mousedown(view, line) {
toggleBreakpoint(view, line.from);
return true;
},
},
initialSpacer: () => null,
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Well-defined custom gutter.

Attaching the mousedown handler to toggle breakpoints is a concise approach. Ensure you document or handle potential double-click scenarios if needed (e.g. toggling off and on quickly).

Comment on lines +12 to +13
#editorProxy: any;
#whenMqttReady: any;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider adding proper types for private fields.

The use of any type reduces type safety. Consider defining proper interfaces for these dependencies.

-  #editorProxy: any;
-  #whenMqttReady: any;
+  #editorProxy: EditorProxy;
+  #whenMqttReady: () => Promise<void>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#editorProxy: any;
#whenMqttReady: any;
#editorProxy: EditorProxy;
#whenMqttReady: () => Promise<void>;

Comment on lines +109 to +117
async deleteRule(path: string) {
return this.#editorProxy.Remove({ path }).then((res: boolean) => {
if (res) {
runInAction(() => {
this.rules = this.rules.filter((rule) => rule.virtualPath !== path);
});
}
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider consistent error handling across operations.

The deleteRule method could benefit from similar error handling as implemented in the save method.

   async deleteRule(path: string) {
-    return this.#editorProxy.Remove({ path }).then((res: boolean) => {
+    try {
+      const res = await this.#editorProxy.Remove({ path });
       if (res) {
         runInAction(() => {
           this.rules = this.rules.filter((rule) => rule.virtualPath !== path);
         });
       }
-    });
+      return res;
+    } catch (error) {
+      throw new Error(error.data === 'MqttConnectionError' 
+        ? 'MQTT connection error' 
+        : error.message);
+    }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async deleteRule(path: string) {
return this.#editorProxy.Remove({ path }).then((res: boolean) => {
if (res) {
runInAction(() => {
this.rules = this.rules.filter((rule) => rule.virtualPath !== path);
});
}
});
}
async deleteRule(path: string) {
try {
const res = await this.#editorProxy.Remove({ path });
if (res) {
runInAction(() => {
this.rules = this.rules.filter((rule) => rule.virtualPath !== path);
});
}
return res;
} catch (error) {
throw new Error(error.data === 'MqttConnectionError'
? 'MQTT connection error'
: error.message);
}
}

}

async save(rule: Rule): Promise<string> {
const path = rule.name.endsWith('.js') ? rule.name : `/${rule.name}.js`;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider extracting path normalization logic.

The path normalization logic could be moved to a utility function for reusability and consistency.

+  private normalizePath(name: string): string {
+    return name.endsWith('.js') ? name : `/${name}.js`;
+  }

   async save(rule: Rule): Promise<string> {
-    const path = rule.name.endsWith('.js') ? rule.name : `/${rule.name}.js`;
+    const path = this.normalizePath(rule.name);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const path = rule.name.endsWith('.js') ? rule.name : `/${rule.name}.js`;
class RulesStore {
// ... other members and methods
private normalizePath(name: string): string {
return name.endsWith('.js') ? name : `/${name}.js`;
}
async save(rule: Rule): Promise<string> {
const path = this.normalizePath(rule.name);
// ... rest of the save implementation
}
// ... other members and methods
}

Comment on lines +58 to +63
if (pathName === 'new') {
return location.replace('/#!/rules/edit/' + savedRuleName);
} else if (rule.initName !== savedRuleName) {
await rulesStore.deleteRule(rule.initName);
return location.replace('/#!/rules/edit/' + savedRuleName);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use template literals for URL construction.

Replace string concatenation with template literals for better readability.

-          return location.replace('/#!/rules/edit/' + savedRuleName);
+          return location.replace(`/#!/rules/edit/${savedRuleName}`);
-          return location.replace('/#!/rules/edit/' + savedRuleName);
+          return location.replace(`/#!/rules/edit/${savedRuleName}`);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (pathName === 'new') {
return location.replace('/#!/rules/edit/' + savedRuleName);
} else if (rule.initName !== savedRuleName) {
await rulesStore.deleteRule(rule.initName);
return location.replace('/#!/rules/edit/' + savedRuleName);
}
if (pathName === 'new') {
return location.replace(`/#!/rules/edit/${savedRuleName}`);
} else if (rule.initName !== savedRuleName) {
await rulesStore.deleteRule(rule.initName);
return location.replace(`/#!/rules/edit/${savedRuleName}`);
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 59-59: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.

(lint/style/useTemplate)


[error] 62-62: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.

(lint/style/useTemplate)

});
}, []);

const createRule = () => location.assign('/#!/rules/new');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider using React Router for navigation.

Using location.assign directly is not ideal in a React application. Consider using React Router's navigation methods.

-  const createRule = () => location.assign('/#!/rules/new');
+  const navigate = useNavigate();
+  const createRule = () => navigate('/rules/new');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const createRule = () => location.assign('/#!/rules/new');
const navigate = useNavigate();
const createRule = () => navigate('/rules/new');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant