ICTU/quality-time

View on GitHub
components/frontend/src/header_footer/Menubar.js

Summary

Maintainability
B
5 hrs
Test Coverage
import "./Menubar.css"

import { element, func, string } from "prop-types"
import { useEffect, useState } from "react"
import FocusLock from "react-focus-lock"
import { Button, Dropdown, Icon, Image, Menu, Message, Portal } from "semantic-ui-react"

import { login, logout } from "../api/auth"
import { Form, Modal, Popup } from "../semantic_ui_react_wrappers"
import { optionalDatePropType, settingsPropType, uiModePropType } from "../sharedPropTypes"
import { Avatar } from "../widgets/Avatar"
import { DatePicker } from "../widgets/DatePicker"
import { CollapseButton } from "./CollapseButton"
import { DownloadAsPDFButton } from "./DownloadAsPDFButton"
import { ResetSettingsButton } from "./ResetSettingsButton"
import { UIModeMenu } from "./UIModeMenu"

function Login({ set_user }) {
    const [username, setUsername] = useState("")
    const [password, setPassword] = useState("")
    const [error, setError] = useState("")

    function submit() {
        login(username, password)
            .then(function (json) {
                if (json.ok) {
                    set_user(username, json.email, new Date(Date.parse(json.session_expiration_datetime)))
                } else {
                    setError("credentials")
                }
            })
            .catch(function (_error) {
                setError("connection")
            })
    }

    let messageHeader = "Heads up"
    let messageContent =
        "Changes you make after you log in, such as adding metrics, changing metric targets, and marking issues as false positive, are logged."
    if (error === "connection") {
        messageHeader = "Connection error"
        messageContent = "Can't reach the server. Please check your connection."
    }
    if (error === "credentials") {
        messageHeader = "Invalid credentials"
        messageContent = "Username and/or password are invalid. Please try again."
    }
    return (
        <Modal
            trigger={
                <Button secondary>
                    <Icon name="user" />
                    Login
                </Button>
            }
            size="tiny"
            onClose={() => setError("")}
        >
            <Modal.Header content="Login" />
            <Modal.Content>
                <Form error={!!error} warning={!error} onSubmit={() => submit()}>
                    <Form.Input
                        autoFocus
                        id="username"
                        name="username"
                        label="Username"
                        onChange={(_event, { value }) => setUsername(value)}
                    />
                    <Form.Input
                        id="password"
                        name="password"
                        type="password"
                        label="Password"
                        onChange={(_event, { value }) => setPassword(value)}
                    />
                    <Message error={!!error} warning={!error} header={messageHeader} content={messageContent} />
                    <Form.Button>Submit</Form.Button>
                </Form>
            </Modal.Content>
        </Modal>
    )
}
Login.propTypes = {
    set_user: func,
}

function Logout({ user, email, set_user }) {
    const trigger = (
        <>
            <Avatar email={email} /> {user}
        </>
    )
    return (
        <Dropdown
            trigger={trigger}
            options={[
                {
                    key: "logout",
                    text: "Logout",
                    icon: "log out",
                    onClick: () => {
                        logout().then(() => set_user(null))
                    },
                },
            ]}
        />
    )
}
Logout.propTypes = {
    user: string,
    email: string,
    set_user: func,
}

export function Menubar({
    email,
    handleDateChange,
    onDate,
    openReportsOverview,
    panel,
    report_date,
    report_uuid,
    settings,
    set_user,
    setUIMode,
    uiMode,
    user,
}) {
    const [settingsPanelVisible, setSettingsPanelVisible] = useState(false)
    useEffect(() => {
        function closePanels(event) {
            if (event.key === "Escape") {
                setSettingsPanelVisible(false)
            }
        }
        window.addEventListener("keydown", closePanels)
        return () => {
            window.removeEventListener("keydown", closePanels)
        }
    }, [])

    const atReportsOverview = report_uuid === ""
    return (
        <>
            <Menu fluid className="menubar" inverted fixed="top">
                <Menu.Menu position="left">
                    <Popup
                        content="Go to reports overview"
                        disabled={atReportsOverview}
                        trigger={
                            <div
                                onBeforeInput={(event) => {
                                    event.preventDefault()
                                    setSettingsPanelVisible(false)
                                    openReportsOverview()
                                }}
                                tabIndex={atReportsOverview ? -1 : 0}
                            >
                                <Menu.Item
                                    header
                                    onClick={
                                        atReportsOverview
                                            ? null
                                            : () => {
                                                  setSettingsPanelVisible(false)
                                                  openReportsOverview()
                                              }
                                    }
                                >
                                    <Image size="mini" src="/favicon.ico" alt="Go home" />
                                    <span style={{ paddingLeft: "6mm", fontSize: "2em" }}>Quality-time</span>
                                </Menu.Item>
                            </div>
                        }
                    />
                    <FocusLock group="settingsPanel" disabled={!settingsPanelVisible} className="center">
                        <div
                            onBeforeInput={(event) => {
                                event.preventDefault()
                                setSettingsPanelVisible(!settingsPanelVisible)
                            }}
                        >
                            <Menu.Item
                                onClick={(event) => {
                                    event.stopPropagation()
                                    setSettingsPanelVisible(!settingsPanelVisible)
                                }}
                                tabIndex={0}
                            >
                                <Icon size="large" name={`caret ${settingsPanelVisible ? "down" : "right"}`} />
                                Settings
                            </Menu.Item>
                        </div>
                    </FocusLock>
                    <Menu.Item>
                        <ResetSettingsButton
                            atReportsOverview={atReportsOverview}
                            handleDateChange={handleDateChange}
                            reportDate={report_date}
                            settings={settings}
                        />
                    </Menu.Item>
                    <Menu.Item>
                        <CollapseButton expandedItems={settings.expandedItems} />
                    </Menu.Item>
                    <Menu.Item>
                        <DownloadAsPDFButton report_uuid={report_uuid} />
                    </Menu.Item>
                </Menu.Menu>
                <Menu.Menu position="right">
                    <Popup
                        content="Show the report as it was on the selected date"
                        position="left center"
                        trigger={
                            <Menu.Item>
                                <Form>
                                    <DatePicker
                                        isClearable={true}
                                        maxDate={new Date()}
                                        onChange={onDate}
                                        selected={report_date}
                                    />
                                </Form>
                            </Menu.Item>
                        }
                    />
                    <Menu.Item>
                        <UIModeMenu setUIMode={setUIMode} uiMode={uiMode} />
                    </Menu.Item>
                    <Menu.Item>
                        {user !== null ? (
                            <Logout email={email} user={user} set_user={set_user} />
                        ) : (
                            <Login set_user={set_user} />
                        )}
                    </Menu.Item>
                </Menu.Menu>
            </Menu>
            <Portal
                closeOnTriggerClick={true}
                open={settingsPanelVisible}
                onClose={(event) => {
                    event.stopPropagation()
                    setSettingsPanelVisible(false)
                }}
                unmountOnHide
            >
                <div className="panel">
                    <FocusLock group="settingsPanel">{panel}</FocusLock>
                </div>
            </Portal>
        </>
    )
}
Menubar.propTypes = {
    email: string,
    handleDateChange: func,
    onDate: func,
    openReportsOverview: func,
    panel: element,
    report_date: optionalDatePropType,
    report_uuid: string,
    settings: settingsPropType,
    set_user: func,
    setUIMode: func,
    uiMode: uiModePropType,
    user: string,
}