summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src/components/projects/ProjectCollection.js
blob: 70f028129b21d73e9d7604bc6430be57c95c9723 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import {
    Gallery,
    Bullseye,
    EmptyState,
    EmptyStateIcon,
    Card,
    CardTitle,
    CardActions,
    DropdownItem,
    CardHeader,
    Dropdown,
    KebabToggle,
    CardBody,
    CardHeaderMain,
    TextVariants,
    Text,
    TextContent,
    Tooltip,
    Button,
    Label,
} from '@patternfly/react-core'
import { PlusIcon, FolderIcon, TrashIcon } from '@patternfly/react-icons'
import PropTypes from 'prop-types'
import React, { useReducer, useMemo } from 'react'
import { Project, Status } from '../../shapes'
import { parseAndFormatDateTime } from '../../util/date-time'
import { AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP, AUTH_NAME_MAP } from '../../util/authorizations'
import NavItemLink from '../util/NavItemLink'
import TableEmptyState from '../util/TableEmptyState'

function ProjectCard({ project, onDelete }) {
    const [isKebabOpen, toggleKebab] = useReducer((t) => !t, false)
    const { id, role, name, updatedAt } = project
    const Icon = AUTH_ICON_MAP[role]

    return (
        <Card
            isCompact
            isRounded
            isFlat
            className="pf-u-min-height"
            style={{ '--pf-u-min-height--MinHeight': '175px' }}
        >
            <CardHeader className="pf-u-flex-grow-1">
                <CardHeaderMain className="pf-u-align-self-flex-start">
                    <FolderIcon />
                </CardHeaderMain>
                <CardActions>
                    <Tooltip content={AUTH_DESCRIPTION_MAP[role]}>
                        <Label icon={<Icon />}>{AUTH_NAME_MAP[role]}</Label>
                    </Tooltip>
                    <Dropdown
                        isPlain
                        position="right"
                        toggle={<KebabToggle className="pf-u-px-0" onToggle={toggleKebab} />}
                        isOpen={isKebabOpen}
                        dropdownItems={[
                            <DropdownItem
                                key="trash"
                                onClick={() => {
                                    onDelete()
                                    toggleKebab()
                                }}
                                position="right"
                                icon={<TrashIcon />}
                            >
                                Delete
                            </DropdownItem>,
                        ]}
                    />
                </CardActions>
            </CardHeader>
            <CardTitle component={NavItemLink} className="pf-u-pb-0" href={`/projects/${id}`}>
                {name}
            </CardTitle>
            <CardBody isFilled={false}>
                <TextContent>
                    <Text component={TextVariants.small}>Last modified {parseAndFormatDateTime(updatedAt)}</Text>
                </TextContent>
            </CardBody>
        </Card>
    )
}

function ProjectCollection({ status, projects, onDelete, onCreate, isFiltering }) {
    const sortedProjects = useMemo(() => {
        const res = [...projects]
        res.sort((a, b) => (new Date(a.updatedAt) < new Date(b.updatedAt) ? 1 : -1))
        return res
    }, [projects])

    if (sortedProjects.length === 0) {
        return (
            <TableEmptyState
                status={status}
                isFiltering={isFiltering}
                loadingTitle="Loading Projects"
                emptyTitle="No projects"
                emptyText="You have not created any projects yet. Create a new project to get started quickly."
                emptyAction={
                    <Button icon={<PlusIcon />} onClick={onCreate}>
                        Create Project
                    </Button>
                }
            />
        )
    }

    return (
        <Gallery hasGutter aria-label="Available projects">
            {sortedProjects.map((project) => (
                <ProjectCard key={project.id} project={project} onDelete={() => onDelete(project)} />
            ))}
            <Card isCompact isFlat isRounded style={{ borderStyle: 'dotted' }}>
                <Bullseye>
                    <EmptyState>
                        <Button isBlock variant="link" onClick={onCreate}>
                            <EmptyStateIcon icon={PlusIcon} />
                            <br />
                            Create Project
                        </Button>
                    </EmptyState>
                </Bullseye>
            </Card>
        </Gallery>
    )
}

ProjectCollection.propTypes = {
    status: Status.isRequired,
    isFiltering: PropTypes.bool,
    projects: PropTypes.arrayOf(Project).isRequired,
    onDelete: PropTypes.func,
    onCreate: PropTypes.func,
}

export default ProjectCollection