-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Display a list of archetypes - Simple column components for Tags, Maintainers, Applications - Filtering on name - Action button is in place but doesn't do anything - TODO items in sources for things that need to be added later Signed-off-by: Scott J Dickerson <[email protected]>
- Loading branch information
Showing
3 changed files
with
291 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,291 @@ | ||
import React from "react"; | ||
import { useTranslation } from "react-i18next"; | ||
import { useHistory } from "react-router-dom"; | ||
import { | ||
Button, | ||
ButtonVariant, | ||
EmptyState, | ||
EmptyStateBody, | ||
EmptyStateFooter, | ||
EmptyStateHeader, | ||
EmptyStateIcon, | ||
Label, | ||
LabelGroup, | ||
PageSection, | ||
PageSectionVariants, | ||
Text, | ||
TextContent, | ||
Toolbar, | ||
ToolbarContent, | ||
ToolbarGroup, | ||
ToolbarItem, | ||
} from "@patternfly/react-core"; | ||
import { Table, Tbody, Th, Thead, Tr, Td } from "@patternfly/react-table"; | ||
import { CubesIcon } from "@patternfly/react-icons"; | ||
|
||
import { AppPlaceholder } from "@app/components/AppPlaceholder"; | ||
import { ConditionalRender } from "@app/components/ConditionalRender"; | ||
import { FilterToolbar, FilterType } from "@app/components/FilterToolbar"; | ||
import { NotificationsContext } from "@app/components/NotificationsContext"; | ||
import { | ||
ConditionalTableBody, | ||
TableHeaderContentWithControls, | ||
TableRowContentWithControls, | ||
} from "@app/components/TableControls"; | ||
import { useLocalTableControls } from "@app/hooks/table-controls"; | ||
import { useFetchArchetypes } from "@app/queries/archetypes"; | ||
import { Archetype, Tag, TagCategory } from "@app/api/models"; | ||
import { LabelCustomColor } from "@migtools/lib-ui"; | ||
import { COLOR_HEX_VALUES_BY_NAME } from "@app/Constants"; | ||
|
||
const Archetypes: React.FC = () => { | ||
const { t } = useTranslation(); | ||
const history = useHistory(); | ||
const { pushNotification } = React.useContext(NotificationsContext); | ||
|
||
const { archetypes, isFetching, error: fetchError } = useFetchArchetypes(); | ||
|
||
const tableControls = useLocalTableControls({ | ||
idProperty: "id", | ||
items: archetypes, | ||
isLoading: isFetching, | ||
isSelectable: false, | ||
expandableVariant: null, | ||
hasActionsColumn: true, | ||
|
||
columnNames: { | ||
name: t("terms.name"), | ||
description: t("terms.description"), | ||
tags: t("terms.tags"), | ||
maintainers: t("terms.maintainers"), | ||
applications: t("terms.applications"), | ||
}, | ||
|
||
filterCategories: [ | ||
{ | ||
key: "name", | ||
title: t("terms.name"), | ||
type: FilterType.search, | ||
placeholderText: | ||
t("actions.filterBy", { | ||
what: t("terms.name").toLowerCase(), | ||
}) + "...", | ||
getItemValue: (archetype) => { | ||
return archetype?.name ?? ""; | ||
}, | ||
}, | ||
// TODO: Add filter for archetype tags | ||
], | ||
|
||
sortableColumns: ["name"], | ||
getSortValues: (archetype) => ({ | ||
name: archetype.name ?? "", | ||
}), | ||
initialSort: { columnKey: "name", direction: "asc" }, | ||
|
||
hasPagination: false, // TODO: Add pagination | ||
}); | ||
const { | ||
currentPageItems, | ||
numRenderedColumns, | ||
propHelpers: { | ||
toolbarProps, | ||
filterToolbarProps, | ||
paginationToolbarItemProps, | ||
paginationProps, | ||
tableProps, | ||
getThProps, | ||
getTdProps, | ||
}, | ||
} = tableControls; | ||
|
||
// Note: Only Architect (and Administrator) personas should be able to access this | ||
// page, therefore no explicit RBAC access checks to be done within the component | ||
|
||
const CreateButton = () => ( | ||
<Button | ||
type="button" | ||
id="create-new-archetype" | ||
aria-label="Create new archetype" | ||
variant={ButtonVariant.primary} | ||
onClick={() => {}} // TODO: Add create archetype modal | ||
> | ||
{t("dialog.title.newArchetype")} | ||
</Button> | ||
); | ||
|
||
return ( | ||
<> | ||
<PageSection variant={PageSectionVariants.light}> | ||
<TextContent> | ||
<Text component="h1">{t("terms.archetypes")}</Text> | ||
</TextContent> | ||
</PageSection> | ||
<PageSection> | ||
<ConditionalRender | ||
when={isFetching && !(archetypes || fetchError)} | ||
then={<AppPlaceholder />} | ||
> | ||
<div | ||
style={{ | ||
backgroundColor: "var(--pf-v5-global--BackgroundColor--100)", | ||
}} | ||
> | ||
<Toolbar {...toolbarProps}> | ||
<ToolbarContent> | ||
<FilterToolbar {...filterToolbarProps} /> | ||
<ToolbarGroup variant="button-group"> | ||
<ToolbarItem> | ||
<CreateButton /> | ||
</ToolbarItem> | ||
</ToolbarGroup> | ||
{/* TODO: Add pagination */} | ||
</ToolbarContent> | ||
</Toolbar> | ||
|
||
<Table | ||
{...tableProps} | ||
id="archetype-table" | ||
aria-label="Archetype table" | ||
> | ||
<Thead> | ||
<Tr> | ||
<TableHeaderContentWithControls {...tableControls}> | ||
<Th {...getThProps({ columnKey: "name" })} /> | ||
<Th {...getThProps({ columnKey: "description" })} /> | ||
<Th {...getThProps({ columnKey: "tags" })} /> | ||
<Th {...getThProps({ columnKey: "maintainers" })} /> | ||
<Th {...getThProps({ columnKey: "applications" })} /> | ||
</TableHeaderContentWithControls> | ||
</Tr> | ||
</Thead> | ||
<ConditionalTableBody | ||
isLoading={isFetching} | ||
isError={!!fetchError} | ||
isNoData={currentPageItems.length === 0} | ||
noDataEmptyState={ | ||
<EmptyState variant="sm"> | ||
<EmptyStateHeader | ||
titleText="No archetypes have been created" | ||
headingLevel="h2" | ||
icon={<EmptyStateIcon icon={CubesIcon} />} | ||
/> | ||
<EmptyStateBody> | ||
Create a new archetype to get started. | ||
</EmptyStateBody> | ||
<EmptyStateFooter> | ||
<CreateButton /> | ||
</EmptyStateFooter> | ||
</EmptyState> | ||
} | ||
numRenderedColumns={numRenderedColumns} | ||
> | ||
{currentPageItems?.map((archetype, rowIndex) => ( | ||
<Tbody key={archetype.id}> | ||
<Tr> | ||
<TableRowContentWithControls | ||
{...tableControls} | ||
item={archetype} | ||
rowIndex={rowIndex} | ||
> | ||
<Td {...getTdProps({ columnKey: "name" })}> | ||
{archetype.name} | ||
</Td> | ||
<Td {...getTdProps({ columnKey: "description" })}> | ||
{/* TODO: Truncate length and add tooltip with full text */} | ||
{archetype.description} | ||
</Td> | ||
<Td {...getTdProps({ columnKey: "tags" })}> | ||
<ArchetypeTagsColumn archetype={archetype} /> | ||
</Td> | ||
<Td {...getTdProps({ columnKey: "maintainers" })}> | ||
<ArchetypeMaintainersColumn archetype={archetype} /> | ||
</Td> | ||
<Td {...getTdProps({ columnKey: "applications" })}> | ||
<ArchetypeApplicationsColumn archetype={archetype} /> | ||
</Td> | ||
<Td>{/* TODO: Add kebab action menu */}</Td> | ||
</TableRowContentWithControls> | ||
</Tr> | ||
</Tbody> | ||
))} | ||
</ConditionalTableBody> | ||
</Table> | ||
|
||
return <div>Very much a place holder.</div>; | ||
{/* TODO: Add pagination */} | ||
</div> | ||
</ConditionalRender> | ||
</PageSection> | ||
|
||
{/* action modals */} | ||
</> | ||
); | ||
}; | ||
|
||
export default Archetypes; | ||
|
||
// | ||
// TODO: Move column components to their own files | ||
// | ||
|
||
// copied from application-tag-label.tsx | ||
export const getTagCategoryFallbackColor = (category?: TagCategory) => { | ||
if (!category?.id) return COLOR_HEX_VALUES_BY_NAME.gray; | ||
const colorValues = Object.values(COLOR_HEX_VALUES_BY_NAME); | ||
return colorValues[category?.id % colorValues.length]; | ||
}; | ||
|
||
// copied from application-tag-label.tsx | ||
const TagLabel: React.FC<{ | ||
tag: Tag; | ||
category?: TagCategory; | ||
}> = ({ tag, category }) => ( | ||
<LabelCustomColor | ||
color={category?.colour || getTagCategoryFallbackColor(category)} | ||
> | ||
{tag.name} | ||
</LabelCustomColor> | ||
); | ||
|
||
// TODO: Refactor the application-tags-label.tsx so applications and archetypes can share `TagLabel` | ||
// TODO: Sort tags? | ||
// TODO: Group tags by categories? | ||
const ArchetypeTagsColumn: React.FC<{ archetype: Archetype }> = ({ | ||
archetype, | ||
}) => ( | ||
<LabelGroup> | ||
{archetype.archetypeTags?.map((tag) => <TagLabel key={tag.id} tag={tag} />)} | ||
</LabelGroup> | ||
); | ||
|
||
// TODO: Don't show the full name, generate initials | ||
// TODO: Sort individual stakeholders with stakeholder groups | ||
// TODO: Add tooltips for each Label with the full name | ||
const ArchetypeMaintainersColumn: React.FC<{ archetype: Archetype }> = ({ | ||
archetype, | ||
}) => ( | ||
<LabelGroup> | ||
{archetype.stakeholders?.map((sh) => <Label key={sh.id}>{sh.name}</Label>)} | ||
{archetype.stakeholderGroups?.map((shg) => ( | ||
<Label key={shg.id}>{shg.name}</Label> | ||
))} | ||
</LabelGroup> | ||
); | ||
|
||
// TODO: When count > 0 render a link to navigate to the application inventory assessment page | ||
// with filters set to show the applications for the archetype? | ||
const ArchetypeApplicationsColumn: React.FC<{ archetype: Archetype }> = ({ | ||
archetype, | ||
}) => { | ||
const { t } = useTranslation(); | ||
|
||
return (archetype?.applications?.length ?? 0) > 0 ? ( | ||
<Text> | ||
{t("message.archetypeApplicationCount", { | ||
count: archetype.applications?.length ?? 0, | ||
})} | ||
</Text> | ||
) : ( | ||
<Text>{t("message.archetypeNoApplications")}</Text> | ||
); | ||
}; |