Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage-community/plugin-microsoft-calendar': minor
---

migrate plugin from mui to bui
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { microsoftCalendarApiRef } from '../src';
import responseMock from './mock.json';
import { microsoftAuthApiRef } from '@backstage/core-plugin-api';
import { Content, Page } from '@backstage/core-components';
import Grid from '@material-ui/core/Grid';
import { Grid } from '@backstage/ui';

createDevApp()
.registerPlugin(microsoftCalendarPlugin)
Expand Down Expand Up @@ -50,9 +50,11 @@ createDevApp()
element: (
<Page themeId="home">
<Content>
<Grid item xs={12} md={6}>
<MicrosoftCalendarCard />
</Grid>
<Grid.Root columns={{ sm: '12' }} gap="6">
<Grid.Item colSpan={{ sm: '12', md: '6' }}>
<MicrosoftCalendarCard />
</Grid.Item>
</Grid.Root>
</Content>
</Page>
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,16 @@
"@backstage/core-components": "backstage:^",
"@backstage/core-plugin-api": "backstage:^",
"@backstage/errors": "backstage:^",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@backstage/ui": "backstage:^",
"@microsoft/microsoft-graph-types": "^2.25.0",
"@remixicon/react": "^4.2.0",
"@tanstack/react-query": "^4.1.3",
"@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0",
"classnames": "^2.3.1",
"dompurify": "^3.4.0",
"lodash": "^4.17.21",
"luxon": "^3.0.0",
"material-ui-popup-state": "^1.9.3",
"react-aria-components": "^1.0.0",
"react-use": "^17.2.4"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@layer components {
.badge {
position: relative;
display: inline-block;
}

.chip {
display: inline-flex;
align-items: center;
padding: 4px var(--bui-space-2);
border: 1px solid var(--bui-border-primary);
border-radius: 16px;
background-color: transparent;
color: var(--bui-fg-primary);
font-size: 12px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}

.badgeContent {
position: absolute;
right: 10px;
top: 5px;
display: flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
background-color: var(--bui-bg-surface-1);
}

.acceptedIcon {
color: var(--bui-fg-success, #10b981);
}

.declinedIcon {
color: var(--bui-fg-danger, #ef4444);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,28 @@
* limitations under the License.
*/

import Badge from '@material-ui/core/Badge';
import Chip from '@material-ui/core/Chip';
import { makeStyles } from '@material-ui/core/styles';
import CancelIcon from '@material-ui/icons/Cancel';
import CheckIcon from '@material-ui/icons/CheckCircle';
import { RiCloseLine, RiCheckLine } from '@remixicon/react';
import { Attendee, ResponseStatusMap } from '../api';

const useStyles = makeStyles(theme => {
const getIconColor = (responseStatus?: string) => {
if (!responseStatus) return theme.palette.primary.light;

return {
[ResponseStatusMap.accepted]: theme.palette.status.ok,
[ResponseStatusMap.declined]: theme.palette.status.error,
}[responseStatus];
};

return {
responseStatus: {
color: ({ responseStatus }: { responseStatus?: string }) =>
getIconColor(responseStatus),
},
badge: {
right: 10,
top: 5,
'& svg': {
height: 16,
width: 16,
background: theme.palette.common.white,
},
},
};
});
import styles from './AttendeeChip.module.css';

const ResponseIcon = ({ responseStatus }: { responseStatus: string }) => {
if (responseStatus === ResponseStatusMap.accepted) {
return <CheckIcon data-testid="accepted-icon" />;
return (
<RiCheckLine
size={16}
data-testid="accepted-icon"
className={styles.acceptedIcon}
/>
);
}
if (responseStatus === ResponseStatusMap.declined) {
return <CancelIcon data-testid="declined-icon" />;
return (
<RiCloseLine
size={16}
data-testid="declined-icon"
className={styles.declinedIcon}
/>
);
}

return null;
Expand All @@ -64,24 +46,16 @@ type AttendeeChipProps = {
};

export const AttendeeChip = ({ user }: AttendeeChipProps) => {
const classes = useStyles({ responseStatus: user.status?.response || '' });
const responseStatus = user.status?.response || '';

return (
<Badge
classes={{
root: classes.responseStatus,
badge: classes.badge,
}}
badgeContent={
<ResponseIcon responseStatus={user.status?.response || ''} />
}
>
<Chip
size="small"
variant="outlined"
label={user.emailAddress?.address}
color="primary"
/>
</Badge>
<div className={styles.badge}>
<div className={styles.chip}>{user.emailAddress?.address}</div>
{responseStatus && (
<div className={styles.badgeContent}>
<ResponseIcon responseStatus={responseStatus} />
</div>
)}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@layer components {
.titleWrapper {
display: flex;
align-items: center;
gap: var(--bui-space-2);
}

.iconWrapper {
height: 32px;
width: 32px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}

.iconWrapper img {
height: 100%;
width: 100%;
object-fit: contain;
}

.spacer {
width: var(--bui-space-1);
}

.flex {
flex: 1;
}

.loaderContainer {
padding: var(--bui-space-3);
}

.eventsContainer {
padding: var(--bui-space-1);
padding-bottom: 0;
max-height: 602px;
overflow-y: auto;
}

.emptyState {
padding-top: var(--bui-space-3);
padding-bottom: var(--bui-space-3);
text-align: center;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@ import { DateTime } from 'luxon';
import { useState } from 'react';

import { InfoCard, Progress } from '@backstage/core-components';

import Box from '@material-ui/core/Box';
import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
import PrevIcon from '@material-ui/icons/NavigateBefore';
import NextIcon from '@material-ui/icons/NavigateNext';
import { ButtonIcon, Text } from '@backstage/ui';
import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react';

import { useCalendarsQuery, useEventsQuery, useSignIn } from '../hooks';
import calendarIcon from '../icons/calendar.svg';
Expand All @@ -33,6 +29,7 @@ import { CalendarSelect } from './CalendarSelect';
import { SignInContent } from './SignInContent';
import { getStartDate } from './util';
import useAsync from 'react-use/esm/useAsync';
import styles from './CalendarCard.module.css';

export const CalendarCard = () => {
const [date, setDate] = useState(DateTime.now());
Expand Down Expand Up @@ -73,28 +70,34 @@ export const CalendarCard = () => {
<InfoCard
noPadding
title={
<Box display="flex" alignItems="center">
<Box height={32} width={32} mr={1}>
<div className={styles.titleWrapper}>
<div className={styles.iconWrapper}>
<img src={calendarIcon} alt="Microsoft Calendar" />
</Box>
</div>
{isSignedIn ? (
<>
<IconButton onClick={() => changeDay(-1)} size="small">
<PrevIcon />
</IconButton>
<IconButton onClick={() => changeDay(1)} size="small">
<NextIcon />
</IconButton>
<Box mr={0.5} />
<Typography variant="h6">
<ButtonIcon
onClick={() => changeDay(-1)}
icon={<RiArrowLeftSLine size={16} />}
variant="secondary"
aria-label="Previous day"
/>
<ButtonIcon
onClick={() => changeDay(1)}
icon={<RiArrowRightSLine size={16} />}
variant="secondary"
aria-label="Next day"
/>
<div className={styles.spacer} />
<Text variant="body-medium">
{date.toLocaleString({
weekday: 'short',
month: 'short',
day: 'numeric',
})}
</Typography>
</Text>

<Box flex={1} />
<div className={styles.flex} />

<CalendarSelect
calendars={calendars}
Expand All @@ -106,39 +109,37 @@ export const CalendarCard = () => {
/>
</>
) : (
<Typography variant="h6">Agenda</Typography>
<Text variant="body-medium">Agenda</Text>
)}
</Box>
</div>
}
deepLink={{
link: 'https://outlook.office.com/calendar/',
title: 'Go to Calendar',
}}
>
<Box>
<div>
{showLoader && (
<Box py={2}>
<div className={styles.loaderContainer}>
<Progress variant="query" />
</Box>
</div>
)}
{!isSignedIn && isInitialized && (
<SignInContent handleAuthClick={() => signIn(false)} />
)}
{!isEventLoading && !isCalendarLoading && isSignedIn && (
<Box p={1} pb={0} maxHeight={602} overflow="auto">
<div className={styles.eventsContainer}>
{events?.length === 0 && (
<Box pt={2} pb={2}>
<Typography align="center" variant="h6">
No events
</Typography>
</Box>
<div className={styles.emptyState}>
<Text color="secondary">No events</Text>
</div>
)}
{sortBy(events, [getStartDate]).map(event => (
<CalendarEvent key={`${event.id}`} event={event} />
))}
</Box>
</div>
)}
</Box>
</div>
</InfoCard>
);
};
Loading
Loading