Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handoff requests #237

Merged
merged 6 commits into from
Feb 21, 2024
Merged
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
19 changes: 18 additions & 1 deletion backend/app/rest/user_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"client_secret": os.getenv("MAILER_CLIENT_SECRET"),
},
os.getenv("MAILER_USER"),
"Supportive Housing", # must replace
"Supportive Housing",
)
auth_service = AuthService(current_app.logger, user_service, email_service)
blueprint = Blueprint("users", __name__, url_prefix="/users")
Expand Down Expand Up @@ -135,6 +135,23 @@ def create_user():
try:
user = CreateInvitedUserDTO(**request.json)
created_user = user_service.create_invited_user(user)
email = created_user.email

# send an invite email to the created user
email_body = """
Hello {first_name},
<br><br>
You've been invited to the SHOW Resident Managment Platform!
<br><br>
To join, <a href={sign_up_link}>click here</a>
to sign up for an account using the email address this was sent to (<strong>{email}</strong>).
""".format(
first_name=created_user.first_name,
sign_up_link="https://blueprintsupportivehousing.web.app/signup",
email=email,
)
email_service.send_email(email, "You've been invited!", email_body)

return jsonify(created_user.__dict__), 201
except DuplicateUserException as e:
error_message = getattr(e, "message", None)
Expand Down
1 change: 1 addition & 0 deletions frontend/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/components/common/Datepicker/*
1 change: 0 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"apollo-upload-client": "^16.0.0",
"axios": "^0.21.1",
"bootstrap": "^4.6.0",
"chakra-dayzed-datepicker": "^0.2.8",
"date-fns": "^2.30.0",
"dayzed": "^3.2.3",
"framer-motion": "^4.1.17",
Expand Down
161 changes: 161 additions & 0 deletions frontend/src/components/common/Datepicker/components/calendarPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import {
HStack,
VStack,
Heading,
Divider,
SimpleGrid,
Box,
Stack,
} from '@chakra-ui/react';
import { useDayzed, Props as DayzedHookProps } from 'dayzed';
import { ArrowKeysReact } from '../utils/reactKeysArrow';
import React, { useCallback, useMemo } from 'react';
import { CalendarConfigs, DatepickerProps } from '../utils/commonTypes';
import { DatepickerBackBtns, DatepickerForwardBtns } from './dateNavBtns';
import { DayOfMonth } from './dayOfMonth';

export interface CalendarPanelProps extends DatepickerProps {
dayzedHookProps: Omit<DayzedHookProps, 'children' | 'render'>;
configs: CalendarConfigs;
disabledDates?: Set<number>;
onMouseEnterHighlight?: (date: Date) => void;
isInRange?: (date: Date) => boolean | null;
}

export const CalendarPanel: React.FC<CalendarPanelProps> = ({
dayzedHookProps,
configs,
propsConfigs,
disabledDates,
onMouseEnterHighlight,
isInRange,
}) => {
const renderProps = useDayzed(dayzedHookProps);
const { calendars, getBackProps, getForwardProps } = renderProps;

const weekdayNames = useMemo(() => {
const firstDayOfWeek = configs.firstDayOfWeek;
const dayNames = configs.dayNames;
if (firstDayOfWeek && firstDayOfWeek > 0) {
return configs.dayNames
.slice(firstDayOfWeek, dayNames.length)
.concat(dayNames.slice(0, firstDayOfWeek));
}
return dayNames;
}, [configs.firstDayOfWeek, configs.dayNames]);

// looking for a useRef() approach to replace it
const getKeyOffset = useCallback((num: number) => {
const e = document.activeElement;
let buttons = document.querySelectorAll('button');
buttons.forEach((el, i) => {
const newNodeKey = i + num;
if (el === e) {
if (newNodeKey <= buttons.length - 1 && newNodeKey >= 0) {
buttons[newNodeKey].focus();
} else {
buttons[0].focus();
}
}
});
}, []);

const arrowKeysReact = new ArrowKeysReact({
left: () => {
getKeyOffset(-1);
},
right: () => {
getKeyOffset(1);
},
up: () => {
getKeyOffset(-7);
},
down: () => {
getKeyOffset(7);
},
});

if (calendars.length <= 0) {
return null;
}

return (
<Stack
className="datepicker-calendar"
direction={['column', 'column', 'row']}
{...propsConfigs?.calendarPanelProps?.wrapperProps}
{...arrowKeysReact.getEvents()}
>
{calendars.map((calendar, calendarIdx) => {
return (
<VStack
key={calendarIdx}
height="100%"
borderWidth="1px"
padding="0.5rem 0.75rem"
{...propsConfigs?.calendarPanelProps?.contentProps}
>
<HStack {...propsConfigs?.calendarPanelProps?.headerProps}>
<DatepickerBackBtns
calendars={calendars}
getBackProps={getBackProps}
propsConfigs={propsConfigs}
/>
<Heading
size="sm"
minWidth={'5rem'}
textAlign="center"
{...propsConfigs?.dateHeadingProps}
>
{configs.monthNames[calendar.month]} {calendar.year}
</Heading>
<DatepickerForwardBtns
calendars={calendars}
getForwardProps={getForwardProps}
propsConfigs={propsConfigs}
/>
</HStack>
<Divider {...propsConfigs?.calendarPanelProps?.dividerProps} />
<SimpleGrid
columns={7}
spacing={1}
textAlign="center"
{...propsConfigs?.calendarPanelProps?.bodyProps}
>
{weekdayNames.map((day, dayIdx) => (
<Box
fontSize="sm"
fontWeight="semibold"
key={dayIdx}
{...propsConfigs?.weekdayLabelProps}
>
{day}
</Box>
))}
{calendar.weeks.map((week, weekIdx) => {
return week.map((dateObj, index) => {
const key = `${calendar.month}-${calendar.year}-${weekIdx}-${index}`;
if (!dateObj) return <Box key={key} />;
const { date } = dateObj;
return (
<DayOfMonth
key={key}
dateObj={dateObj}
propsConfigs={propsConfigs}
renderProps={renderProps}
isInRange={isInRange && isInRange(date)}
disabledDates={disabledDates}
onMouseEnter={() => {
if (onMouseEnterHighlight) onMouseEnterHighlight(date);
}}
/>
);
});
})}
</SimpleGrid>
</VStack>
);
})}
</Stack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Button, ButtonProps } from '@chakra-ui/react';
import { Calendar, GetBackForwardPropsOptions } from 'dayzed';
import React, { Fragment } from 'react';
import { DatepickerProps } from '../utils/commonTypes';

export interface DatepickerBackBtnsProps extends DatepickerProps {
calendars: Calendar[];
getBackProps: (data: GetBackForwardPropsOptions) => Record<string, any>;
}

const DefaultBtnStyle: ButtonProps = {
variant: 'ghost',
size: 'sm',
};

export const DatepickerBackBtns: React.FC<DatepickerBackBtnsProps> = (
props
) => {
const { calendars, getBackProps } = props;
const customBtnProps = props.propsConfigs?.dateNavBtnProps;
return (
<Fragment>
<Button
{...getBackProps({
calendars,
offset: 12,
})}
{...DefaultBtnStyle}
{...customBtnProps}
>
{'<<'}
</Button>
<Button
{...getBackProps({ calendars })}
{...DefaultBtnStyle}
{...customBtnProps}
>
{'<'}
</Button>
</Fragment>
);
};

export interface DatepickerForwardBtnsProps extends DatepickerProps {
calendars: Calendar[];
getForwardProps: (data: GetBackForwardPropsOptions) => Record<string, any>;
}

export const DatepickerForwardBtns: React.FC<DatepickerForwardBtnsProps> = (
props
) => {
const { calendars, getForwardProps } = props;
const customBtnProps = props.propsConfigs?.dateNavBtnProps;
return (
<Fragment>
<Button
{...getForwardProps({ calendars })}
{...DefaultBtnStyle}
{...customBtnProps}
>
{'>'}
</Button>
<Button
{...getForwardProps({
calendars,
offset: 12,
})}
{...DefaultBtnStyle}
{...customBtnProps}
>
{'>>'}
</Button>
</Fragment>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Button, ButtonProps } from '@chakra-ui/react';
import { DateObj, RenderProps } from 'dayzed';
import React, { useMemo } from 'react';
import { DatepickerProps, DayOfMonthBtnStyleProps } from '../utils/commonTypes';

interface DayOfMonthProps extends DatepickerProps {
renderProps: RenderProps;
isInRange?: boolean | null;
disabledDates?: Set<number>;
dateObj: DateObj;
onMouseEnter?: React.MouseEventHandler<HTMLButtonElement> | undefined;
}

type HoverStyle =
| (ButtonProps['_hover'] & { _disabled: ButtonProps['_disabled'] })
| undefined;

const halfGap = 0.125; //default Chakra-gap-space-1 is 0.25rem

export const DayOfMonth: React.FC<DayOfMonthProps> = ({
dateObj,
propsConfigs,
isInRange,
disabledDates,
renderProps,
onMouseEnter,
}) => {
const { date, selected, selectable, today } = dateObj;
const { getDateProps } = renderProps;
const {
defaultBtnProps,
isInRangeBtnProps,
selectedBtnProps,
todayBtnProps,
} = propsConfigs?.dayOfMonthBtnProps || {};
const disabled = !selectable || disabledDates?.has(date.getTime());
const styleBtnProps: DayOfMonthBtnStyleProps = useMemo(
() => ({
defaultBtnProps: {
size: 'sm',
variant: 'ghost',
// this intends to fill the visual gap from Grid to improve the UX
// so the button active area is actually larger than what it's seen
...defaultBtnProps,
_after: {
content: "''",
position: 'absolute',
top: `-${halfGap}rem`,
left: `-${halfGap}rem`,
bottom: `-${halfGap}rem`,
right: `-${halfGap}rem`,
borderWidth: `${halfGap}rem`,
borderColor: 'transparent',
...defaultBtnProps?._after,
},
_hover: {
bg: 'purple.400',
...defaultBtnProps?._hover,
_disabled: {
bg: 'gray.100',
// temperory hack to persist the typescript checking
...(defaultBtnProps?._hover as HoverStyle)?._disabled,
},
},
},
isInRangeBtnProps: {
background: 'purple.200',
...isInRangeBtnProps,
},
selectedBtnProps: {
background: 'purple.200',
...selectedBtnProps,
},
todayBtnProps: {
borderColor: 'blue.400',
...todayBtnProps,
},
}),
[defaultBtnProps, isInRangeBtnProps, selectedBtnProps, todayBtnProps]
);

return (
<Button
{...getDateProps({
dateObj,
disabled: disabled,
onMouseEnter: onMouseEnter,
})}
isDisabled={disabled}
{...styleBtnProps.defaultBtnProps}
{...(isInRange && !disabled && styleBtnProps.isInRangeBtnProps)}
{...(selected && !disabled && styleBtnProps.selectedBtnProps)}
{...(today && styleBtnProps.todayBtnProps)}
>
{date.getDate()}
</Button>
);
};
6 changes: 6 additions & 0 deletions frontend/src/components/common/Datepicker/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type { DatepickerConfigs, OnDateSelected } from './utils/commonTypes';
export type { CalendarPanelProps } from './components/calendarPanel';
export * from './utils/calanderUtils';
export { CalendarPanel } from './components/calendarPanel';
export * from './single';
export * from './range';
Loading
Loading