Replace name with title, and make card height adjustable
Signed-off-by: Frida Jacobsson <fridahelenajacobsson@gmail.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-bazaar': minor
|
||||
---
|
||||
|
||||
`HomePageBazaarInfoCard` is now displaying `title` instead of `name`. Title is a string that doesn't have to be URL friendly.
|
||||
The BazaarOverviewCard have the new property `fullHeight`. Link in `BazaarOverviewCard`is moved to header in card.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-bazaar-backend': patch
|
||||
---
|
||||
|
||||
Column `title` has replaced column `name` for `BazaarProject` in database
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Update with your config settings.
|
||||
|
||||
// This file makes it possible to run "yarn knex migrate:make some_file_name"
|
||||
// to assist in making new migrations
|
||||
module.exports = {
|
||||
client: 'better-sqlite3',
|
||||
connection: ':memory:',
|
||||
useNullAsDefault: true,
|
||||
migrations: {
|
||||
directory: './migrations',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = async function up(knex) {
|
||||
await knex.schema.alterTable('metadata', table => {
|
||||
table.renameColumn('name', 'title');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.down = async function down(knex) {
|
||||
await knex.schema.alterTable('metadata', table => {
|
||||
table.renameColumn('title', 'name');
|
||||
});
|
||||
};
|
||||
@@ -19,7 +19,7 @@ import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils';
|
||||
import { Knex as KnexType } from 'knex';
|
||||
|
||||
const bazaarProject: any = {
|
||||
name: 'n1',
|
||||
title: 'n1',
|
||||
entityRef: 'ref1',
|
||||
community: '',
|
||||
status: 'proposed',
|
||||
@@ -64,7 +64,7 @@ describe('DatabaseHandler', () => {
|
||||
|
||||
await knex('metadata').insert({
|
||||
entity_ref: bazaarProject.entityRef,
|
||||
name: bazaarProject.name,
|
||||
title: bazaarProject.title,
|
||||
description: bazaarProject.description,
|
||||
community: bazaarProject.community,
|
||||
status: bazaarProject.status,
|
||||
|
||||
@@ -52,7 +52,7 @@ export class DatabaseHandler {
|
||||
private columns = [
|
||||
'metadata.id',
|
||||
'metadata.entity_ref',
|
||||
'metadata.name',
|
||||
'metadata.title',
|
||||
'metadata.description',
|
||||
'metadata.status',
|
||||
'metadata.updated_at',
|
||||
@@ -116,7 +116,7 @@ export class DatabaseHandler {
|
||||
|
||||
async insertMetadata(bazaarProject: any) {
|
||||
const {
|
||||
name,
|
||||
title,
|
||||
entityRef,
|
||||
community,
|
||||
description,
|
||||
@@ -129,7 +129,7 @@ export class DatabaseHandler {
|
||||
|
||||
await this.client
|
||||
.insert({
|
||||
name,
|
||||
title,
|
||||
entity_ref: entityRef,
|
||||
community,
|
||||
description,
|
||||
@@ -145,7 +145,7 @@ export class DatabaseHandler {
|
||||
|
||||
async updateMetadata(bazaarProject: any) {
|
||||
const {
|
||||
name,
|
||||
title,
|
||||
id,
|
||||
entityRef,
|
||||
community,
|
||||
@@ -158,7 +158,7 @@ export class DatabaseHandler {
|
||||
} = bazaarProject;
|
||||
|
||||
return await this.client('metadata').where({ id: id }).update({
|
||||
name,
|
||||
title,
|
||||
entity_ref: entityRef,
|
||||
description,
|
||||
community,
|
||||
|
||||
@@ -80,13 +80,13 @@ export const homePage = (
|
||||
+ </Grid>
|
||||
|
||||
+ <Grid item xs={12} >
|
||||
+ <BazaarOverviewCard order='random' fullWidth />
|
||||
+ <BazaarOverviewCard order='random' fullWidth fullHeight />
|
||||
+ </Grid>
|
||||
|
||||
{/* ...other homepage items */}
|
||||
```
|
||||
|
||||
The property `fullWidth` is optional and can be used to adjust the card to fit a grid with column width 12.
|
||||
The properties `fullHeight` and `fullWidth` are optional and can be used to adjust the cards styling.
|
||||
|
||||
## How does the Bazaar work?
|
||||
|
||||
@@ -106,7 +106,7 @@ To add a project to the bazaar, simply click on the `add-project` button and fil
|
||||
|
||||
The following fields are mandatory:
|
||||
|
||||
- name - name of the project on URL safe format
|
||||
- title - title of the project
|
||||
- description - present your idea and what skills you are looking for
|
||||
- status - whether or not the project has started
|
||||
- size - small, medium or large
|
||||
|
||||
@@ -17,6 +17,7 @@ export const BazaarOverviewCard: (
|
||||
export type BazaarOverviewCardProps = {
|
||||
order: 'latest' | 'random';
|
||||
fullWidth?: boolean;
|
||||
fullHeight?: boolean;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 357 KiB After Width: | Height: | Size: 396 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 1.4 MiB |
@@ -42,8 +42,7 @@ export const AddProjectDialog = ({
|
||||
const [selectedEntity, setSelectedEntity] = useState<Entity | null>(null);
|
||||
|
||||
const defaultValues = {
|
||||
name: '',
|
||||
title: 'Add project',
|
||||
title: '',
|
||||
community: '',
|
||||
description: '',
|
||||
status: 'proposed' as Status,
|
||||
@@ -65,6 +64,7 @@ export const AddProjectDialog = ({
|
||||
reset: UseFormReset<FormValues>,
|
||||
) => {
|
||||
const formValues = getValues();
|
||||
|
||||
const response = await bazaarApi.addProject({
|
||||
...formValues,
|
||||
entityRef: selectedEntity ? stringifyEntityRef(selectedEntity) : null,
|
||||
|
||||
@@ -24,13 +24,16 @@ import type { BazaarProject } from '../../types';
|
||||
import { bazaarApiRef } from '../../api';
|
||||
import { fetchCatalogItems } from '../../util/fetchMethods';
|
||||
import { parseBazaarProject } from '../../util/parseMethods';
|
||||
import { ErrorPanel, InfoCard } from '@backstage/core-components';
|
||||
import { ErrorPanel, InfoCard, Link } from '@backstage/core-components';
|
||||
import { bazaarPlugin } from '../../plugin';
|
||||
import { IconButton } from '@material-ui/core';
|
||||
import StorefrontIcon from '@material-ui/icons/Storefront';
|
||||
|
||||
/** @public */
|
||||
export type BazaarOverviewCardProps = {
|
||||
order: 'latest' | 'random';
|
||||
fullWidth?: boolean;
|
||||
fullHeight?: boolean;
|
||||
};
|
||||
|
||||
const getUnlinkedCatalogEntities = (
|
||||
@@ -48,7 +51,7 @@ const getUnlinkedCatalogEntities = (
|
||||
|
||||
/** @public */
|
||||
export const BazaarOverviewCard = (props: BazaarOverviewCardProps) => {
|
||||
const { order, fullWidth = false } = props;
|
||||
const { order, fullWidth = false, fullHeight = false } = props;
|
||||
const bazaarApi = useApi(bazaarApiRef);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const root = useRouteRef(bazaarPlugin.routes.root);
|
||||
@@ -127,7 +130,13 @@ export const BazaarOverviewCard = (props: BazaarOverviewCardProps) => {
|
||||
title={
|
||||
order === 'latest' ? 'Bazaar Latest Projects' : 'Bazaar Random Projects'
|
||||
}
|
||||
deepLink={bazaarLink}
|
||||
action={
|
||||
<IconButton>
|
||||
<Link to={bazaarLink.link} title={bazaarLink.title}>
|
||||
<StorefrontIcon />
|
||||
</Link>
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ProjectPreview
|
||||
bazaarProjects={bazaarProjects.value || []}
|
||||
@@ -135,6 +144,7 @@ export const BazaarOverviewCard = (props: BazaarOverviewCardProps) => {
|
||||
catalogEntities={unlinkedCatalogEntities || []}
|
||||
useTablePagination={false}
|
||||
gridSize={fullWidth ? 2 : 4}
|
||||
height={fullHeight ? '13rem' : '7rem'}
|
||||
/>
|
||||
</InfoCard>
|
||||
);
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Grid,
|
||||
makeStyles,
|
||||
Card,
|
||||
CardContent,
|
||||
Typography,
|
||||
@@ -31,13 +30,6 @@ import { entityRouteRef } from '@backstage/plugin-catalog-react';
|
||||
import { StatusTag } from '../StatusTag';
|
||||
import { Member, BazaarProject } from '../../types';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
break: {
|
||||
wordBreak: 'break-word',
|
||||
textAlign: 'justify',
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
bazaarProject: BazaarProject;
|
||||
members: Member[];
|
||||
@@ -51,7 +43,6 @@ export const CardContentFields = ({
|
||||
descriptionSize,
|
||||
membersSize,
|
||||
}: Props) => {
|
||||
const classes = useStyles();
|
||||
const catalogEntityRoute = useRouteRef(entityRouteRef);
|
||||
|
||||
return (
|
||||
@@ -64,12 +55,7 @@ export const CardContentFields = ({
|
||||
{bazaarProject.description
|
||||
.split('\n')
|
||||
.map((str: string, i: number) => (
|
||||
<Typography
|
||||
key={i}
|
||||
variant="body2"
|
||||
paragraph
|
||||
className={classes.break}
|
||||
>
|
||||
<Typography key={i} variant="body2" paragraph>
|
||||
{str}
|
||||
</Typography>
|
||||
))}
|
||||
@@ -114,7 +100,6 @@ export const CardContentFields = ({
|
||||
picture={member.picture}
|
||||
/>
|
||||
<Link
|
||||
className={classes.break}
|
||||
target="_blank"
|
||||
to={
|
||||
member.userRef
|
||||
|
||||
@@ -106,8 +106,8 @@ export const EditProjectDialog = ({
|
||||
handleClose={handleDeleteClose}
|
||||
message={[
|
||||
'Are you sure you want to delete ',
|
||||
<b key={bazaarProject.name} className={classes.wordBreak}>
|
||||
{bazaarProject.name}
|
||||
<b key={bazaarProject.id} className={classes.wordBreak}>
|
||||
{bazaarProject.title}
|
||||
</b>,
|
||||
' from the Bazaar?',
|
||||
]}
|
||||
|
||||
@@ -173,7 +173,7 @@ export const EntityBazaarInfoContent = ({
|
||||
{parseEntityRef(bazaarProject.entityRef!).name}
|
||||
</b>,
|
||||
' from ',
|
||||
<b className={classes.wordBreak}>{bazaarProject.name}</b>,
|
||||
<b className={classes.wordBreak}>{bazaarProject.title}</b>,
|
||||
' ?',
|
||||
]}
|
||||
type="unlink"
|
||||
@@ -182,7 +182,7 @@ export const EntityBazaarInfoContent = ({
|
||||
)}
|
||||
|
||||
<CardHeader
|
||||
title={<p className={classes.wordBreak}>{bazaarProject?.name!}</p>}
|
||||
title={<p className={classes.wordBreak}>{bazaarProject?.title!}</p>}
|
||||
action={
|
||||
<div>
|
||||
<IconButton
|
||||
|
||||
@@ -237,7 +237,7 @@ export const HomePageBazaarInfoCard = ({
|
||||
{parseEntityRef(bazaarProject.value?.entityRef!).name}
|
||||
</b>,
|
||||
' from ',
|
||||
<b className={classes.wordBreak}>{bazaarProject.value?.name}</b>,
|
||||
<b className={classes.wordBreak}>{bazaarProject.value?.title}</b>,
|
||||
' ?',
|
||||
]}
|
||||
type="unlink"
|
||||
@@ -257,7 +257,7 @@ export const HomePageBazaarInfoCard = ({
|
||||
<CardHeader
|
||||
title={
|
||||
<p className={classes.wordBreak}>
|
||||
{bazaarProject.value?.name || initProject.name}
|
||||
{bazaarProject.value?.title || initProject.title}
|
||||
</p>
|
||||
}
|
||||
action={
|
||||
|
||||
@@ -30,7 +30,7 @@ type Rules = {
|
||||
};
|
||||
|
||||
type Props = {
|
||||
inputType: 'description' | 'community' | 'responsible' | 'name';
|
||||
inputType: 'description' | 'community' | 'responsible' | 'title';
|
||||
error?: FieldError | undefined;
|
||||
control: Control<FormValues, object>;
|
||||
helperText?: string;
|
||||
|
||||
@@ -34,7 +34,7 @@ type Props = {
|
||||
project: BazaarProject;
|
||||
fetchBazaarProjects: () => Promise<BazaarProject[]>;
|
||||
catalogEntities: Entity[];
|
||||
fullHeight?: boolean;
|
||||
height?: string;
|
||||
};
|
||||
|
||||
const useStyles = makeStyles({
|
||||
@@ -48,7 +48,6 @@ const useStyles = makeStyles({
|
||||
WebkitLineClamp: 7,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
textAlign: 'justify',
|
||||
},
|
||||
memberCount: {
|
||||
float: 'right',
|
||||
@@ -60,6 +59,7 @@ const useStyles = makeStyles({
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
height: '5rem',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -67,10 +67,11 @@ export const ProjectCard = ({
|
||||
project,
|
||||
fetchBazaarProjects,
|
||||
catalogEntities,
|
||||
height,
|
||||
}: Props) => {
|
||||
const classes = useStyles();
|
||||
const [openCard, setOpenCard] = useState(false);
|
||||
const { id, name, status, updatedAt, description, membersCount } = project;
|
||||
const { id, title, status, updatedAt, description, membersCount } = project;
|
||||
|
||||
const handleClose = () => {
|
||||
setOpenCard(false);
|
||||
@@ -93,7 +94,7 @@ export const ProjectCard = ({
|
||||
classes={{ root: classes.header }}
|
||||
title={
|
||||
<Typography noWrap variant="h6" component="h4">
|
||||
{name}
|
||||
{title}
|
||||
</Typography>
|
||||
}
|
||||
subtitle={`updated ${DateTime.fromISO(
|
||||
@@ -102,7 +103,7 @@ export const ProjectCard = ({
|
||||
base: DateTime.now(),
|
||||
})}`}
|
||||
/>
|
||||
<CardContent className={classes.content}>
|
||||
<CardContent className={classes.content} style={{ height: height }}>
|
||||
<StatusTag styles={classes.statusTag} status={status} />
|
||||
<Typography variant="body2" className={classes.memberCount}>
|
||||
{Number(membersCount) === Number(1)
|
||||
|
||||
@@ -89,16 +89,14 @@ export const ProjectDialog = ({
|
||||
</CustomDialogTitle>
|
||||
<DialogContent style={{ padding: '1rem', paddingTop: '0rem' }} dividers>
|
||||
<InputField
|
||||
error={errors.name}
|
||||
error={errors.title}
|
||||
control={control}
|
||||
rules={{
|
||||
required: true,
|
||||
pattern: RegExp('^[a-zA-Z0-9_-]*$'),
|
||||
}}
|
||||
inputType="name"
|
||||
helperText="please enter a url safe project name"
|
||||
inputType="title"
|
||||
helperText="Please enter a title for your project"
|
||||
/>
|
||||
|
||||
<InputField
|
||||
error={errors.description}
|
||||
control={control}
|
||||
@@ -106,7 +104,7 @@ export const ProjectDialog = ({
|
||||
required: true,
|
||||
}}
|
||||
inputType="description"
|
||||
helperText="please enter a description"
|
||||
helperText="Please enter a description"
|
||||
/>
|
||||
|
||||
<InputSelector
|
||||
@@ -128,7 +126,7 @@ export const ProjectDialog = ({
|
||||
required: true,
|
||||
}}
|
||||
inputType="responsible"
|
||||
helperText="please enter a contact person"
|
||||
helperText="Please enter a contact person"
|
||||
placeholder="Contact person of the project"
|
||||
/>
|
||||
|
||||
@@ -142,7 +140,7 @@ export const ProjectDialog = ({
|
||||
pattern: RegExp('^(https?)://'),
|
||||
}}
|
||||
inputType="community"
|
||||
helperText="please enter a link starting with http/https"
|
||||
helperText="Please enter a link starting with http/https"
|
||||
placeholder="Community link to e.g. Teams or Discord"
|
||||
/>
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ type Props = {
|
||||
catalogEntities: Entity[];
|
||||
useTablePagination?: boolean;
|
||||
gridSize?: GridSize;
|
||||
height: string;
|
||||
};
|
||||
|
||||
const useStyles = makeStyles({
|
||||
@@ -55,6 +56,7 @@ export const ProjectPreview = ({
|
||||
catalogEntities,
|
||||
useTablePagination = true,
|
||||
gridSize = 2,
|
||||
height,
|
||||
}: Props) => {
|
||||
const classes = useStyles();
|
||||
const [page, setPage] = useState(1);
|
||||
@@ -90,6 +92,7 @@ export const ProjectPreview = ({
|
||||
key={i}
|
||||
fetchBazaarProjects={fetchBazaarProjects}
|
||||
catalogEntities={catalogEntities}
|
||||
height={height}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
|
||||
@@ -27,7 +27,7 @@ import { BazaarProject } from '../../types';
|
||||
import { bazaarApiRef } from '../../api';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import SearchBar from 'material-ui-search-bar';
|
||||
import { sortByDate, sortByMembers, sortByName } from '../../util/sortMethods';
|
||||
import { sortByDate, sortByMembers, sortByTitle } from '../../util/sortMethods';
|
||||
import { SortMethodSelector } from '../SortMethodSelector';
|
||||
import { fetchCatalogItems } from '../../util/fetchMethods';
|
||||
import { parseBazaarProject } from '../../util/parseMethods';
|
||||
@@ -71,7 +71,7 @@ export const SortView = () => {
|
||||
const bazaarApi = useApi(bazaarApiRef);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const classes = useStyles();
|
||||
const sortMethods = [sortByDate, sortByName, sortByMembers];
|
||||
const sortMethods = [sortByDate, sortByTitle, sortByMembers];
|
||||
const [sortMethodNbr, setSortMethodNbr] = useState(0);
|
||||
const [openAdd, setOpenAdd] = useState(false);
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
@@ -99,7 +99,7 @@ export const SortView = () => {
|
||||
|
||||
const getSearchResults = () => {
|
||||
return bazaarProjects.value
|
||||
?.filter(project => project.name.includes(searchValue))
|
||||
?.filter(project => project.title.includes(searchValue))
|
||||
.sort(sortMethods[sortMethodNbr]);
|
||||
};
|
||||
|
||||
@@ -199,6 +199,7 @@ export const SortView = () => {
|
||||
bazaarProjects={getSearchResults() || []}
|
||||
fetchBazaarProjects={fetchBazaarProjects}
|
||||
catalogEntities={unlinkedCatalogEntities || []}
|
||||
height="13rem"
|
||||
/>
|
||||
<Content noPadding className={classes.container} />
|
||||
</Content>
|
||||
|
||||
@@ -27,7 +27,7 @@ export type Status = 'ongoing' | 'proposed';
|
||||
export type Size = 'small' | 'medium' | 'large';
|
||||
|
||||
export type BazaarProject = {
|
||||
name: string;
|
||||
title: string;
|
||||
id: number;
|
||||
entityRef?: string;
|
||||
community: string;
|
||||
@@ -42,7 +42,7 @@ export type BazaarProject = {
|
||||
};
|
||||
|
||||
export type FormValues = {
|
||||
name: string;
|
||||
title: string;
|
||||
description: string;
|
||||
community: string;
|
||||
status: string;
|
||||
|
||||
@@ -20,7 +20,7 @@ export const parseBazaarProject = (metadata: any): BazaarProject => {
|
||||
return {
|
||||
id: metadata.id,
|
||||
entityRef: metadata.entity_ref,
|
||||
name: metadata.name,
|
||||
title: metadata.title,
|
||||
community: metadata.community,
|
||||
description: metadata.description,
|
||||
status: metadata.status,
|
||||
|
||||
@@ -26,10 +26,10 @@ export const sortByDate = (a: BazaarProject, b: BazaarProject): number => {
|
||||
return dateB - dateA;
|
||||
};
|
||||
|
||||
export const sortByName = (a: BazaarProject, b: BazaarProject) => {
|
||||
if (a.name < b.name) {
|
||||
export const sortByTitle = (a: BazaarProject, b: BazaarProject) => {
|
||||
if (a.title < b.title) {
|
||||
return -1;
|
||||
} else if (a.name > b.name) {
|
||||
} else if (a.title > b.title) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user