Giter Club home page Giter Club logo

maas-ui's Introduction

MAAS UI

CI Playwright Tests accessibility Cypress sitespeed.io Commitizen friendly

About

MAAS is an open-source tool that lets you build a data centre from bare-metal servers. You can discover, commission, deploy, and dynamically reconfigure a large network of individual units.

screenshot of MAAS UI displaying 1000 machines

This repository contains the sourcecode for the MAAS web app, maas-ui.

MAAS UI Overview

MAAS UI Overview

Contributing

Community contributions are most welcome, and there are a number of ways to participate:

When submitting a PR, please take note that MAAS UI uses the conventional commit format. To help you conform to this, you can run yarn commit instead of git commit for an interactive prompt.

Please see HACKING for details on setting up a MAAS UI development environment.

Feedback

  • Ask a question about MAAS on Discourse.
  • File a MAAS issue.
    • If you think that the issue is related to the UI, please add a ui tag

Integration testing

Integration testing

Release Process

Release Process

Related Projects

MAAS

MAAS server source and issue tracking can be found on Launchpad.

LXD

LXD is a next generation system container and virtual machine manager, used extensively by MAAS.

Built With

Team Members

MAAS Tribe and Canonical Web & Design

Code of Conduct

This project adopts the Ubuntu Code of Conduct.

License

Code licensed AGPLv3 by Canonical Ltd.

With โ™ฅ from Canonical

maas-ui's People

Contributors

amylily1011 avatar andesol avatar anthonydillon avatar bartaz avatar bethcollins92 avatar caleb-ellis avatar clementchaumel avatar dependabot[bot] avatar dgtlntv avatar hatched avatar huwshimi avatar jay-topher avatar lyubomir-popov avatar ndv99 avatar petermakowski avatar petesfrench avatar r00ta avatar renovate-bot avatar renovate[bot] avatar sowasred2012 avatar sparkiegeek avatar squidsoup avatar steverydz avatar tai271828 avatar tdorsey avatar tmerten avatar troyanov avatar vtapia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maas-ui's Issues

Create new table component

Create a table component using v2 vanilla that covers the use cases from the juju-react-components table.

Hardcoded values instead of variables

The new maas-ui repo is relying consistently on hardcoded values, with no references to any of the vanilla variables. In some places there are at least //TODO notes to replace with variables.

In others it is just magic numbers.

If these fix vanilla problems, please create an issue on the vanilla repo so I can later address and remove the override.

If they are newly built components, please use the vanilla spacing variables, so we can make things consistent.

Errors when deleting users are not dispatched

The websocket messages are:
{"method":"user.delete","type":0,"params":{"id":2},"request_id":2} and {"type": 1, "request_id": 2, "rtype": 1, "error": "(\"Cannot delete some instances of model 'User' be....

For some reason a DELETE_USERS_ERRORS is not dispatched with that error.

Make basename optional

Make the basename optional for dev. There's currently an error if it's not set:

Module../src/index.js
src/index.js:19
  16 | const sagaMiddleware = createSagaMiddleware();
  17 | // This UI will be deployed at /MAAS/settings for now, so route everthing to that.
  18 | export const history = createBrowserHistory({
> 19 |   basename: `${MAAS_config.ui.basename}/settings` // eslint-disable-line no-undef
  20 | });
  21 | const composeEnhancers = composeWithDevTools({});
  22 | const middleware = [sagaMiddleware, routerMiddleware(history)];

State may change while a user is editing a form

Note: this isn't really a bug, depends on your perspective.

While sync messages correctly update state, and components correctly re-render with state changes from selectors, form fields are not updated with new values.

Steps to Reproduce

  1. Open a tab with /MAAS/configuration/general in maas-ui.
  2. Open a tab with MAAS/settings/general/ in old maas.
  3. Update MAAS name.
  4. Confirm that state is updated in maas-ui.
  5. Confirm that the component re-renders with the new value from the selector (a console.log should suffice).
  6. Confirm that the field does not get updated.

Simple Solution

The problem can be trivially solved with this solution, but I'm not sure we want to do that.

I've confirmed that adding enableReinitialize to <Formik> forms works however.
2019-08-16 16 43 56

Unfortunately, I think this has UX issues, as having the form automatically update would be surprising and potentially confusing for users (most web apps are not realtime). Changing data for the input the user is currently editing being the worst case scenario!

Discussion

On one hand, changing form data while a form is being edited is surprising, but equally problematic is having a user submit changes without awareness that the state has changed while they have been editing the form.

Would some visual indication to the user (other than the data changing) that the app has received an update from the server help?

Would it be better to give the user a visual indication that the data has changed and provide a way for the user to manually refresh the form?

Is this a UX problem we could give some thought to @lilyvidenova @cassiocassio?

Github has a nice simple model for this with diffs, when a diff is outdated, there's a just a wee label displayed indicating the current view is out of date. Doing anything more than that (e.g. indicating which fields have changed), would add a lot of additional complexity.

Split base files

Base selectors, actions and reducers are all in single files. These need to be split to handle the multitude of models we will have.

Add window title

Display the current page in the window title + fetch and display the maas name in the format [page name] | [maas name] MAAAS e.g. "Settings | mymaas MAAS".

Add filterTerm to state for relevant entities

In RBAC we had a separate field in state for filter term. That way we could compose a selector for search that would include the filter term and a subset/mutated list of entities (e.g repositories.getForSettings or something that just renames the defaults). We should consider if we want to use the same pattern here. Regardless of the approach we should fix the original issue of repositories not being filtered by "display name".

Relevant Q in reselect README: https://github.com/reduxjs/reselect#q-how-do-i-create-a-selector-that-takes-an-argument

Settings/Users: Searching doesn't update pagination

To reproduce

  1. Visit /MAAS/users/
  2. Search for a user.
  3. Note that the pagination count is not updated (there will be n empty pages) - this is particularly confusing if you're already on a page other than 1.

Make settings url configurable

When deployed in MAAS the URL for settings won't not be /settings so we need to be able to configure the basename to match.

Refactor redux actions

Currently we pass nested redux payloads to our websockets layer, e.g.:

repositories.fetch = () => {
  return {
    type: "WEBSOCKET_SEND",
    payload: {
      actionType: "FETCH_REPOSITORIES",
      message: {
        method: "packagerepository.list",
        params: { limit: 50 },
        type: 0
      }
    }
  };
};

This does have the advantage of making the saga code very succinct, but it is a bit unusual and in contravention of flux standard actions. Instead, we should probably dispatch a payload that looks more like:

repositories.fetch = () => (
{
  type: "FETCH_REPOSITORIES",
  meta: {
    limit: 50
  }
})     

That way the redux actions have no knowledge of the websocket implementation, which can be encapsulated in a saga (type, params, method etc).

ProxyForm: Selecting "External" or "Peer" results in error

Traceback:

index.js:1375 Warning: `value` prop on `input` should not be null. Consider using an empty string to clear the component or `undefined` for uncontrolled components.
    in input (at Input.js:33)
    in div (at Field.js:48)
    in div (at Field.js:88)
    in Field (at Input.js:22)
    in Input (at FormikField.js:16)
    in FormikField (at ProxyFormFields.js:37)
    in ProxyFormFields (at ProxyForm.js:93)
    in form (at Form.js:6)
    in Form (at ProxyForm.js:92)
    in Formik (at ProxyForm.js:46)
    in div (at Col.js:45)
    in Col (at ProxyForm.js:43)
    in div (at Row.js:6)
    in Row (at ProxyForm.js:42)
    in ProxyForm (created by Context.Consumer)
    in Route (at Routes.js:34)
    in Switch (at Routes.js:21)
    in Routes (at Settings.js:15)
    in div (at Col.js:45)
    in Col (at Section.js:25)
    in div (at Row.js:6)
    in Row (at Strip.js:41)
    in header (at Strip.js:24)
    in Strip (at Section.js:14)
    in div (at Section.js:10)
    in Section (at Settings.js:14)
    in Settings (created by Context.Consumer)
    in Route (at Routes.js:8)
    in Routes (at Main.js:29)
    in Main (created by ConnectFunction)
    in ConnectFunction (at App.js:26)
    in App (created by ConnectFunction)
    in ConnectFunction (at src/index.js:30)
    in StrictMode (at src/index.js:29)
    in Router (created by ConnectedRouter)
    in ConnectedRouter (created by Context.Consumer)
    in ConnectedRouterWithContext (created by ConnectFunction)
    in ConnectFunction (at src/index.js:28)
    in Provider (at src/index.js:27)

Related, Formik returns a default error message when the fields are submitted and null, which could be friendlier.

Error: httpProxy must be a string type, but the final value was: null. If "null" is intended as an empty value be sure to mark the schema as .nullable()

Find suitable pattern for setting formik errors from redux state in useEffect

Currently to update formik with errors from redux state, we disable react-hooks/exhaustive-dep instead of passing formikProps as a dependency, as otherwise useEffect is called in an infinite loop.

  useEffect(() => {
    if (Object.keys(errors).length) {
      const formikErrors = {};
      const invalidValues = {};
      Object.keys(errors).forEach(field => {
        formikErrors[field] = errors[field].join(" ");
        invalidValues[field] = formikProps.values[field];
      });
      formikProps.setStatus({ serverErrors: formikErrors, invalidValues });
    }
  }, [errors]); // eslint-disable-line react-hooks/exhaustive-deps

Create prod/dev/demo configs

Create default configurations for prod, dev and the demo service. Maybe using environment variables to load the correct config.

Client stuck on loading if issue parsing csrf token on server

If maas server has csrf authentication enabled, the client will sit in a "loading" state indefinitely without an error.

The client should be raising a PROTOCOL_ERROR with the error "Invalid CSRF token", but this is not surfaced to the client.

Keep `general` state from becoming stale

Relevant PR comments: #133 (review)

At the moment, general data is only fetchable and in many cases its in some way derived from config state. We need a way to re-fetch general state whenever config is synced (UPDATE_CONFIG_NOTIFY).

Only fetch/list once

Currently we use the hook useFetchOnce to ensure data is only fetched once per model. This should

Add pagination to MainTable

To reduce duplication in the table views the pagination controls could be moved into the MainTable component.

Fetch the CSRF token from the cookie

Use the cookie to get the CSRF token when deployed alongside MAAS. A CSRF token should also be able to be provided in dev when the cookie is not available.

ActionButton should properly reflect redux state

At the moment the button doesn't actually reflect state. Instead it assumes moving from saving: true to saving: false means the action was successful, which is pretty naive.
There should be some value in state that is set to true when a form submission is successful (or when config is synced) that the ActionButton receives.
The ActionButon should still have some internal state that determines how long to show the spinner and success icons, otherwise the spinner duration could be almost instant, and the success duration could be forever.

Add delete dhcp snippet confirmation

  • Create a confirmation to delete a dhcp snippet (as an expanding row).
  • Create new redux action to delete a dhcp snippet.
  • Dispatch action on clicking delete, and close confirmation if successful.

Populate DHCP snippets table

Create a selector for dhcp snippets and display the data in a table with edit/delete buttons (non-functional).

Handle incoming websocket messages

When changes happen in MAAS e.g. from the CLE, then presumably there are websocket messages sent to the client which are not handled by the normal get/request implementation we have now.

This might require having a mapping of request types to action type e.g. users.list to UPDATE_USERS, but we need to investigate what the messages contain.

Config not fetched if app loaded from /users

Config is never fetched if app loaded from /users or non config route, and config components relying on state never load.

Steps to reproduce

  1. Load app from /MAAS/users
  2. Visit /MAAS/configuration/general

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.