reactstrap#Card TypeScript Examples
The following examples show how to use
reactstrap#Card.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: app.tsx From website with Apache License 2.0 | 5 votes |
function App({
query,
loading,
onSearch,
chartsData,
}: AppPropType): JSX.Element {
return (
<>
<div id="plot-container">
<Container fluid={true}>
<Row>
<Card className="place-options-card">
<Container className="place-options" fluid={true}>
<div className="place-options-section">
<TextSearchBar
onSearch={onSearch}
initialValue={query}
placeholder='For example "People with Doctorate Degrees in France"'
/>
</div>
</Container>
</Card>
</Row>
{chartsData && !loading && (
<Row>
<Card>
<div id="main" className="col-md-9x col-lg-10">
<StatVarResults {...chartsData} />
</div>
</Card>
</Row>
)}
<div id="screen" style={{ display: loading ? "block" : "none" }}>
<div id="spinner"></div>
</div>
</Container>
</div>
</>
);
}
Example #2
Source File: stat_vars.tsx From website with Apache License 2.0 | 5 votes |
/**
* Component for rendering results associated with a rich search query.
*/
export function StatVarResults({
placeName,
statVarInfo,
statVarOrder,
debug,
}: StatVarResultsPropType): JSX.Element {
const [debugOpen, setDebugOpen] = useState(false);
const places = Object.keys(placeName);
if (!places.length) {
return (
<section className="block col-12">No places found in the query.</section>
);
}
if (!statVarOrder.length) {
return <section className="block col-12">No results found.</section>;
}
return (
<section className="block col-12">
<div id="chart-region">
<ChartRegion
placeName={placeName}
statVarInfo={statVarInfo}
statVarOrder={statVarOrder}
></ChartRegion>
</div>
<ul>
{statVarOrder.map((sv) => (
<li key={sv}>
<a href={getURL(places, sv)}>{statVarInfo[sv].title}</a>
</li>
))}
</ul>
{!!debug.length && (
<div className="debug-view">
<Button
className="btn-light"
onClick={() => setDebugOpen(!debugOpen)}
size="sm"
>
{debugOpen ? "Hide Debug" : "Show Debug"}
</Button>
<Collapse isOpen={debugOpen}>
<Card>
<CardBody>
{debug.map((line, key) => (
<div key={key}>{line}</div>
))}
</CardBody>
</Card>
</Collapse>
</div>
)}
</section>
);
}
Example #3
Source File: place_details.tsx From website with Apache License 2.0 | 5 votes |
export function PlaceDetails(props: PlaceDetailsPropType): JSX.Element {
const selectedPlace = props.placeInfo.selectedPlace;
const unitString = _.isEmpty(props.unit) ? "" : ` ${props.unit}`;
const selectedPlaceValue =
selectedPlace.dcid in props.breadcrumbDataValues
? formatNumber(props.breadcrumbDataValues[selectedPlace.dcid], "") +
unitString
: "N/A";
const selectedPlaceDate =
selectedPlace.dcid in props.metadata
? ` (${props.metadata[selectedPlace.dcid].placeStatDate})`
: "";
const rankedPlaces = props.geoJsonFeatures.filter(
(feature) =>
feature.properties.geoDcid in props.mapDataValues &&
_.isNumber(props.mapDataValues[feature.properties.geoDcid])
);
rankedPlaces.sort(
(a, b) =>
props.mapDataValues[b.properties.geoDcid] -
props.mapDataValues[a.properties.geoDcid]
);
return (
<Card className="place-details-card">
<div className="place-details">
<div className="place-details-section">
<div className="place-details-section-title">Top Places</div>
{rankedPlaces
.slice(0, Math.min(5, rankedPlaces.length))
.map((place, index) =>
getListItemElement(
{
dcid: place.properties.geoDcid,
name: place.properties.name || place.properties.geoDcid,
types: [props.placeInfo.enclosedPlaceType],
},
props,
unitString,
index + 1
)
)}
</div>
<div className="place-details-section">
<div className="place-details-section-title">Bottom Places</div>
{rankedPlaces.slice(-5).map((place, index) =>
getListItemElement(
{
dcid: place.properties.geoDcid,
name: place.properties.name || place.properties.geoDcid,
types: [props.placeInfo.enclosedPlaceType],
},
props,
unitString,
Math.max(0, rankedPlaces.length - 5) + index + 1
)
)}
</div>
<div className="place-details-section">
<div className="place-details-section-title">Containing Places</div>
<div>
{selectedPlace.name}
{selectedPlaceDate}: {selectedPlaceValue}
</div>
{props.placeInfo.parentPlaces.map((place) =>
getListItemElement(place, props, unitString)
)}
</div>
</div>
</Card>
);
}
Example #4
Source File: Home.tsx From reference-merchant with Apache License 2.0 | 5 votes |
function Home(props) {
const { t } = useTranslation("layout");
const [selectedProduct, setSelectedProduct] = useState<Product>();
const [products, setProducts] = useState<Product[] | undefined>();
const [demoMode, setDemoMode] = useState<boolean>(props.demoMode === undefined ? false : true);
const getProducts = async () => {
try {
setProducts(await new BackendClient().getProductsList());
} catch (e) {
console.error(e);
}
};
useEffect(() => {
//noinspection JSIgnoredPromiseFromCall
getProducts();
}, []);
return (
<>
<TestnetWarning />
<Container>
<h1 className="text-center font-weight-bold mt-5">{t("name")}</h1>
<section className="mt-5">
{products && (
<Row>
{products.map((product, i) => (
<Col key={product.gtin} md={6} lg={4}>
<Card key={product.gtin} className="mb-4">
<CardImg top src={product.image_url} />
<CardBody>
<CardTitle className="font-weight-bold h5">{product.name}</CardTitle>
<CardText>{product.description}</CardText>
</CardBody>
<CardFooter>
<Row>
<Col>
<div>
<strong>Price:</strong> {product.price / 1000000} {product.currency}
</div>
</Col>
<Col lg={4} className="text-right">
<Button
color="secondary"
block
className="btn-sm"
onClick={() => setSelectedProduct(products[i])}
>
Buy Now
</Button>
</Col>
</Row>
</CardFooter>
</Card>
</Col>
))}
</Row>
)}
{!products && <ProductsLoader />}
</section>
</Container>
<Payment
demoMode={demoMode}
product={selectedProduct}
isOpen={!!selectedProduct}
onClose={() => setSelectedProduct(undefined)}
/>
</>
);
}
Example #5
Source File: DataVisualization.tsx From TutorBase with MIT License | 4 votes |
DataVisualization = () => {
const [dropdownLabel2, setDropdownLabel2] = useState("All Time");
const [dropdownOpen, setDropdownOpen] = useState(false);
const [dropdownOpen2, setDropdownOpen2] = useState(false);
const [dropdownOpen3, setDropdownOpen3] = useState(false);
const [dateRange, setDateRange] = useState(new Date(2020,0,0));
const [course, setCourse] = useState("All Courses");
const [courses, setCourses] = useState(new Array<string>());
const [appointments, setAppointments] = useState(0);
const [hours, setHours] = useState(0);
const [earnings, setEarnings] = useState(0);
const [chart, setChart] = useState(0);
const [meetingsMap, setMeetingsMap] = useState(new Map<number,number>());
const [earningsMap, setEarningsMap] = useState(new Map<number,number>());
const toggle = () => setDropdownOpen(prevState => !prevState);
const toggle2 = () => setDropdownOpen2(prevState => !prevState);
const toggle3 = () => setDropdownOpen3(prevState => !prevState);
let tutor = useSelector(selectClientData);
let tutorID = tutor.clientId;
useEffect(() => {
GetTutoringHours(course, tutorID).then( apiResult => {
setMeetingsMap(apiResult[0]);
setEarningsMap(apiResult[1]);
setAppointments(apiResult[3]);
setHours(apiResult[2]);
setEarnings(apiResult[4]);
setCourses(apiResult[5]);
});
},[]);
let coursesDropdowns:Array<ReactElement> = [];
coursesDropdowns.push(<DropdownItem onClick={() => {
setCourse("All Courses");
GetTutoringHours("All Courses", tutorID).then( apiResult => {
setMeetingsMap(apiResult[0]);
setEarningsMap(apiResult[1]);
setAppointments(apiResult[3]);
setHours(apiResult[2]);
setEarnings(apiResult[4]);
setCourses(apiResult[5]);
});
}}>
All Courses
</DropdownItem>);
for (let i = 0; i < courses.length; i++) {
coursesDropdowns.push(<DropdownItem onClick={() => {
setCourse(courses[i]);
GetTutoringHours(courses[i], tutorID).then( apiResult => {
setMeetingsMap(apiResult[0]);
setEarningsMap(apiResult[1]);
setAppointments(apiResult[3]);
setHours(apiResult[2]);
setEarnings(apiResult[4]);
setCourses(apiResult[5]);
});
}}>
{courses[i]}
</DropdownItem>);
}
return (
<Container fluid className="background" style={{marginBottom:'10em'}}>
<hr></hr>
<Row xs="2" className="parent">
</Row>
<div style={{display:'flex', flexDirection:'row', flexWrap:'wrap'}}>
<div style={{display:'flex', flexDirection:'column', flex:'1 1 0px', flexWrap:'wrap'}}>
<Card body>
<CardTitle tag="h5">Appointments</CardTitle>
<CardText>
<h1>
<CountUp
end={appointments}
useEasing={true}
duration={3.5}
/>
</h1>
</CardText>
</Card>
</div>
<div style={{display:'flex', flexDirection:'column', flex:'1 1 0px', flexWrap:'wrap'}}>
<Card body>
<CardTitle tag="h5">Hours Tutored</CardTitle>
<CardText>
<h1>
<CountUp
end={hours}
useEasing={true}
duration={4}
/>
</h1>
</CardText>
</Card>
</div>
<div style={{display:'flex', flexDirection:'column', flex:'1 1 0px', flexWrap:'wrap'}}>
<Card body>
<CardTitle tag="h5">Earnings</CardTitle>
<CardText>
<h1>
<CountUp
decimals={2}
prefix="$"
end={earnings}
useEasing={true}
duration={4}/>
</h1>
</CardText>
</Card>
</div>
</div>
<div style={{display:'flex', flexDirection:'row'}}>
<Card body>
<CardTitle tag="h5">
<div style={{display:'flex', flexDirection:'row', flexWrap:'wrap'}}>
<div style={{display:'flex', flexDirection:'column', marginRight:'1em', marginTop:'0.25em'}}>
<Dropdown isOpen={dropdownOpen} toggle={toggle}>
<DropdownToggle caret>
{(chart === 0) ? "Calendar" : (chart === 1 ? "Total Hours" : "Total Earnings")}
<FontAwesomeIcon icon={faArrowDown} style={{marginLeft:'1em'}}/>
</DropdownToggle>
<DropdownMenu>
<DropdownItem header>Tutor Data</DropdownItem>
<DropdownItem onClick={() => setChart(0)}>Calendar</DropdownItem>
<DropdownItem onClick={() => setChart(1)}>Total Hours</DropdownItem>
<DropdownItem divider />
<DropdownItem onClick={() => setChart(2)}>Total Earnings</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
{ chart != 0 ?
<div style={{display:'flex', flexDirection:'column', marginRight:'1em', marginTop:'0.25em'}}>
<Dropdown isOpen={dropdownOpen2} toggle={toggle2} style={{alignSelf:'right'}}>
<DropdownToggle caret>
{dropdownLabel2}
<FontAwesomeIcon icon={faArrowDown} style={{marginLeft:'1em'}}/>
</DropdownToggle>
<DropdownMenu>
<DropdownItem header>Date Range</DropdownItem>
<DropdownItem onClick={() => {
let date = new Date(getNow());
date.setFullYear(2020);
date.setMonth(0);
date.setDate(0);
setDateRange(date);
setDropdownLabel2("All Time");
}}>
All Time
</DropdownItem>
<DropdownItem onClick={() => {
let date = new Date(getNow());
date.setFullYear(date.getFullYear() - 1);
setDateRange(date);
setDropdownLabel2("1Y");
}}>1Y
</DropdownItem>
<DropdownItem onClick={() => {
let date = new Date(getNow());
date.setMonth(date.getMonth() - 6);
setDateRange(date);
setDropdownLabel2("6M");
}}>6M
</DropdownItem>
<DropdownItem onClick={() => {
let date = new Date(getNow());
date.setMonth(date.getMonth() - 1);
setDateRange(date);
setDropdownLabel2("1M");
}}>1M
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
: <div></div>}
<div style={{display:'flex', flexDirection:'column', marginTop:'0.25em'}}>
<Dropdown isOpen={dropdownOpen3} toggle={toggle3} style={{alignSelf:'right'}}>
<DropdownToggle caret>
{course}
<FontAwesomeIcon icon={faArrowDown} style={{marginLeft:'1em'}}/>
</DropdownToggle>
<DropdownMenu>
<DropdownItem header>Filter by Course</DropdownItem>
{coursesDropdowns}
</DropdownMenu>
</Dropdown>
</div>
</div>
</CardTitle>
<CardText>
{chart == 0 ?
<TutorHeatmap dateMap={meetingsMap} />
: (chart == 1 ? <LineGraph dateMap={meetingsMap}
fromTime={dateRange}
isHours={true}/>
:<LineGraph dateMap={earningsMap}
fromTime={dateRange}
isHours={false}/>
)}
</CardText>
</Card>
</div>
</Container>
);
}
Example #6
Source File: TutorPanelSignup.tsx From TutorBase with MIT License | 4 votes |
Panel = (props: IProps) => {
let dispatch = useDispatch();
let id = useSelector(selectClientData).clientId;
const [modalOpen, setModalOpen] = useState(false);
let params : string = useLocation().pathname;
const [selectedSubjects, setSelectedSubjects] = useState(new Set<string>());
const [RIN, setRIN] = useState("");
const [validRIN, setValidRIN] = useState(false);
const [cohort, setCohort] = useState("");
const [comments, setComments] = useState("");
const [footerMessage, setFooterMessage] = useState("");
const [rate, setRate] = useState(0);
let subjects = [];
let selectedSubjectsOutput = [];
const [subjectsList, setSubjectsList] = useState(new Array<Subject>());
function checkRIN(value: string) {
if (value.length !== 9) {
setValidRIN(false);
}
else {
setValidRIN(true);
}
setRIN(value);
}
function submit() {
if (!validRIN
|| cohort === ""
|| cohort === "Select"
|| selectedSubjects.size === 0) {
setFooterMessage("Please complete required fields.");
return;
}
let subs: Array<String> = Array.from(selectedSubjects.keys());
api.TutorSignup(id, RIN, subs, comments, rate).then(res =>{
res ?
setFooterMessage("Application submitted.")
: setFooterMessage("Error submitting. Please try again.");
}).catch(err => {
setFooterMessage("Error submitting. Please try again.")
});
}
useEffect(() => {
// Get all avaliable subjects from API
const getSubjects = async () => {
return (await api.GetSubjects()).data;
}
getSubjects().then(value => {
setSubjectsList(value);
}
)
}, []);
for (let i = 0; i < subjectsList.length; i++) {
let name: string = subjectsList[i].id;
let color = SubjectToColor(name);
subjects.push(
(<Button
style={{background: color}}
onClick={() => setSelectedSubjects(SelectedSubjectsHandler(selectedSubjects, name))}
>
{name}
</Button>
)
);
}
let selectedSubs:Array<string> = Array.from(selectedSubjects.keys());
for (let i = 0; i < selectedSubs.length; i++) {
let name: string = selectedSubs[i];
let color = SubjectToColor(name);
selectedSubjectsOutput.push(
(
<Badge
style={{
backgroundColor: color,
cursor:'default',
color: "black",
minWidth: '6em',
display: "flex",
flexDirection:'row',
alignItems: 'center',
marginRight: '0.5em'
}}
pill
>
<div style={{
display: "flex",
flex: '50%',
}}>
{name + ' '}
</div>
<Button
close
style={{
display: "flex",
flex: '50%',
alignItems: 'center'
}}
onClick={() => setSelectedSubjects(SelectedSubjectsHandler(selectedSubjects, name))} />
</Badge>
)
);
}
return (
<div id="panel-wrapper">
<Navbar className={classNames("navbar-expand-lg", "navbar-light", "bg-light", "border-bottom", "shadow")}>
<Button className="btn-red" id="menu-toggle" onClick={() => {
dispatch(actions.toggleSidebar());
}}>☰</Button>
</Navbar>
<Container fluid className="background" style={{marginBottom:'10em'}}>
<hr></hr>
<Row xs="2" className="parent">
</Row>
<div style={{display:'flex', flexDirection:'column', flexWrap:'wrap', alignContent:'center'}}>
{props.isLoading ? (
<div style={{display:'flex', flexDirection:'row', flex:'1 1 0px', flexWrap:'wrap', justifyContent:'center', marginTop:'10em'}}>
<Spinner style={{color:'#E66064'}}></Spinner>
</div>)
: (
<div>
<div style={{display:'flex', flexDirection:'row', flex:'1 1 0px', flexWrap:'wrap', justifyContent:'center', marginTop:'10em'}}>
<h5>You are not currently signed up as a tutor. This dashboard is for tutors only. You can apply to be a TutorBase tutor below!
</h5></div>
<div style={{display:'flex', flexDirection:'row', flex:'1 1 0px', flexWrap:'wrap', justifyContent:'center', marginTop:'1em'}}>
<Button
className="btn-red"
style={{height:'4em', width:'10em', borderRadius:'20em'}}
onClick={() => setModalOpen(true)}
>
Sign up as tutor
</Button>
<Modal
centered={true}
scrollable={true}
isOpen={modalOpen}
>
<ModalHeader toggle={() => setModalOpen(!modalOpen)}>
Tutor Application Form
</ModalHeader>
<ModalBody>
<h5>RIN</h5>
<Input
defaultValue={RIN}
onChange={(e) => checkRIN(e.target.value)}
valid={validRIN}
invalid={!validRIN}
/>
<p />
<h5>Cohort</h5>
<Input
type="select"
onChange={(e) => setCohort(e.target.value)}
initialValue="Select"
invalid={cohort === "" || cohort === "Select"}>
<option>
Select
</option>
<option>
Freshman
</option>
<option>
Sophomore
</option>
<option>
Junior
</option>
<option>
Senior
</option>
<option>
Graduate
</option>
</Input>
<p />
<h5>Select Subjects to tutor</h5>
<ButtonGroup>
{subjects}
</ButtonGroup>
<p>
Selected:
<Card
outline={selectedSubjects.size === 0}
color= {selectedSubjects.size === 0 ? "danger" : ""}>
<CardBody
style={{
display: "flex",
background: "lightgray",
minHeight: "4em",
flexWrap: 'wrap'
}}>
{selectedSubjectsOutput}
</CardBody></Card>
</p>
<p>
<h5>Hourly Rate ($) (optional)</h5>
<Input
type="number"
onChange={(e) => setRate(+(e.target.value))} />
</p>
<h5>Comments (optional)</h5>
<Input
type="textarea"
onChange={(e) => setComments(e.target.value)} />
</ModalBody>
<ModalFooter>
<p style={{color: footerMessage === "Application submitted." ? 'green' : 'red'}}>
{footerMessage}
</p>
<Button
color="primary"
onClick={() => submit()}
>
Submit
</Button>
{' '}
<Button onClick={() => setModalOpen(false)}>
Cancel
</Button>
</ModalFooter>
</Modal>
</div>
</div>
)}
</div>
</Container>
</div>
);
}
Example #7
Source File: place_selector.tsx From website with Apache License 2.0 | 4 votes |
export function PlaceSelector(props: PlaceSelectorProps): JSX.Element {
const [childPlaceTypes, setChildPlaceTypes] = useState([]);
useEffect(() => {
if (_.isNull(props.selectedPlace.types)) {
return;
}
loadChildPlaceTypes();
}, [props.selectedPlace]);
return (
<Card className={`${SELECTOR_PREFIX}-card`}>
<Container className={`${SELECTOR_PREFIX}-container`} fluid={true}>
<div className={`${SELECTOR_PREFIX}-main-selectors`}>
<div
className={`${SELECTOR_PREFIX}-section`}
id={`${SELECTOR_PREFIX}-search-section`}
>
<div className={`${SELECTOR_PREFIX}-label`}>Plot places in</div>
<SearchBar
places={
props.selectedPlace.dcid
? { [props.selectedPlace.dcid]: props.selectedPlace.name }
: {}
}
addPlace={(e) => selectPlace(e, props.onPlaceSelected)}
removePlace={() =>
unselectPlace(props.onPlaceSelected, setChildPlaceTypes)
}
numPlacesLimit={1}
customPlaceHolder={props.customSearchPlaceholder}
/>
</div>
<div className={`${SELECTOR_PREFIX}-section`}>
<div className={`${SELECTOR_PREFIX}-label`}>of type</div>
<div>
<CustomInput
id={`${SELECTOR_PREFIX}-place-type`}
type="select"
value={props.enclosedPlaceType}
onChange={(e) =>
selectEnclosedPlaceType(e, props.onEnclosedPlaceTypeSelected)
}
className="pac-target-input"
>
<option value="">Select a place type</option>
{childPlaceTypes.map((type) => (
<option value={type} key={type}>
{ENCLOSED_PLACE_TYPE_NAMES[type] || type}
</option>
))}
</CustomInput>
</div>
</div>
</div>
{props.children}
</Container>
</Card>
);
function loadChildPlaceTypes(): void {
getParentPlacesPromise(props.selectedPlace.dcid)
.then((parentPlaces) => {
const getEnclosedPlaceTypesFn =
props.getEnclosedPlaceTypes || getEnclosedPlaceTypes;
const newChildPlaceTypes = getEnclosedPlaceTypesFn(
props.selectedPlace,
parentPlaces
);
if (_.isEqual(newChildPlaceTypes, childPlaceTypes)) {
return;
}
if (_.isEmpty(newChildPlaceTypes)) {
alert(
`Sorry, we don't support ${props.selectedPlace.name}. Please select a different place.`
);
} else if (newChildPlaceTypes.length === 1) {
props.onEnclosedPlaceTypeSelected(newChildPlaceTypes[0]);
}
setChildPlaceTypes(newChildPlaceTypes);
})
.catch(() => setChildPlaceTypes([]));
}
}
Example #8
Source File: chart.tsx From website with Apache License 2.0 | 4 votes |
export function Chart(props: ChartProps): JSX.Element {
const statVar = props.statVar.value;
const [errorMessage, setErrorMessage] = useState("");
const mainSvInfo: StatVarInfo =
statVar.dcid in statVar.info ? statVar.info[statVar.dcid] : {};
const title = getTitle(
Array.from(props.dates),
mainSvInfo.title || statVar.dcid,
statVar.perCapita
);
const placeDcid = props.placeInfo.enclosingPlace.dcid;
const statVarDcid = statVar.dcid;
const [mapPoints, setMapPoints] = useState(null);
const [mapPointsFetched, setMapPointsFetched] = useState(false);
const [zoomTransformation, setZoomTransformation] = useState(
DEFAULT_ZOOM_TRANSFORMATION
);
const chartContainerRef = useRef<HTMLDivElement>();
// load mapPoints in the background.
useEffect(() => {
props.mapPointsPromise
.then((mapPoints) => {
setMapPoints(mapPoints);
setMapPointsFetched(true);
})
.catch(() => setMapPointsFetched(true));
}, []);
function replot() {
draw(
props,
setErrorMessage,
mapPoints,
zoomTransformation,
setZoomTransformation,
props.display.value.color,
props.display.value.domain
);
}
// Replot when data changes.
useEffect(() => {
if (props.display.value.showMapPoints && !mapPointsFetched) {
loadSpinner(SECTION_CONTAINER_ID);
return;
} else {
removeSpinner(SECTION_CONTAINER_ID);
}
replot();
}, [props, mapPointsFetched]);
// Replot when chart width changes on sv widget toggle.
useEffect(() => {
const debouncedHandler = _.debounce(() => {
if (!props.display.value.showMapPoints || mapPointsFetched) {
replot();
}
}, DEBOUNCE_INTERVAL_MS);
const resizeObserver = new ResizeObserver(debouncedHandler);
if (chartContainerRef.current) {
resizeObserver.observe(chartContainerRef.current);
}
return () => {
resizeObserver.unobserve(chartContainerRef.current);
debouncedHandler.cancel();
};
}, [props, chartContainerRef]);
return (
<div className="chart-section-container">
<Card className="chart-section-card">
<Container id={SECTION_CONTAINER_ID} fluid={true}>
<div className="chart-section">
<div className="map-title">
<h3>
{title}
{props.dates.size > 1 && (
<span
onMouseOver={onDateRangeMouseOver}
onMouseOut={onDateRangeMouseOut}
id={DATE_RANGE_INFO_ID}
>
<i className="material-icons-outlined">info</i>
</span>
)}
</h3>
<div id={DATE_RANGE_INFO_TEXT_ID}>
The date range represents the dates of the data shown in this
map.
</div>
</div>
{errorMessage ? (
<div className="error-message">{errorMessage}</div>
) : (
<div className="map-section-container">
<div id={CHART_CONTAINER_ID} ref={chartContainerRef}>
<div id={MAP_CONTAINER_ID}></div>
<div id={LEGEND_CONTAINER_ID}></div>
</div>
<div className="zoom-button-section">
<div id={ZOOM_IN_BUTTON_ID} className="zoom-button">
<i className="material-icons">add</i>
</div>
<div id={ZOOM_OUT_BUTTON_ID} className="zoom-button">
<i className="material-icons">remove</i>
</div>
</div>
</div>
)}
{props.display.value.showTimeSlider &&
props.sampleDates &&
props.sampleDates.length > 1 && (
<TimeSlider
currentDate={_.max(Array.from(props.dates))}
dates={props.sampleDates}
metahash={props.metahash}
onPlay={props.onPlay}
startEnabled={props.dates.size === 1}
updateDate={props.updateDate}
/>
)}
<div className="map-links">
{mainSvInfo.ranked && (
<a className="explore-timeline-link" href={props.rankingLink}>
<span className="explore-timeline-text">
Explore rankings
</span>
<i className="material-icons">keyboard_arrow_right</i>
</a>
)}
{!mainSvInfo.ranked &&
(props.placeInfo.selectedPlace.dcid in props.mapDataValues ||
props.placeInfo.selectedPlace.dcid in
props.breadcrumbDataValues) && (
<a
className="explore-timeline-link"
href={`/tools/timeline#place=${placeDcid}&statsVar=${statVarDcid}`}
>
<span className="explore-timeline-text">
Explore timeline
</span>
<i className="material-icons">keyboard_arrow_right</i>
</a>
)}
</div>
</div>
</Container>
</Card>
<ToolChartFooter
chartId="map"
sources={props.sources}
mMethods={null}
sourceSelectorSvInfoList={[props.sourceSelectorSvInfo]}
onSvMetahashUpdated={(svMetahashMap) =>
props.statVar.setMetahash(svMetahashMap[props.statVar.value.dcid])
}
hideIsRatio={false}
isPerCapita={props.statVar.value.perCapita}
onIsPerCapitaUpdated={(isPerCapita: boolean) =>
props.statVar.setPerCapita(isPerCapita)
}
>
{props.placeInfo.mapPointPlaceType && (
<div className="chart-option">
<FormGroup check>
<Label check>
<Input
id="show-installations"
type="checkbox"
checked={props.display.value.showMapPoints}
onChange={(e) =>
props.display.setShowMapPoints(e.target.checked)
}
/>
Show Installations
</Label>
</FormGroup>
</div>
)}
</ToolChartFooter>
<div id="map-chart-screen" className="screen">
<div id="spinner"></div>
</div>
</div>
);
}
Example #9
Source File: chart.tsx From website with Apache License 2.0 | 4 votes |
export function Chart(props: ChartPropsType): JSX.Element {
const svgContainerRef = useRef<HTMLDivElement>();
const tooltipRef = useRef<HTMLDivElement>();
const chartContainerRef = useRef<HTMLDivElement>();
const mapLegendRef = useRef<HTMLDivElement>();
const [geoJson, setGeoJson] = useState(null);
const [geoJsonFetched, setGeoJsonFetched] = useState(false);
const xDates: Set<string> = new Set();
const yDates: Set<string> = new Set();
Object.values(props.points).forEach((point) => {
xDates.add(point.xDate);
yDates.add(point.yDate);
});
const xTitle = getTitle(Array.from(xDates), props.xLabel);
const yTitle = getTitle(Array.from(yDates), props.yLabel);
// Tooltip needs to start off hidden
d3.select(tooltipRef.current)
.style("visibility", "hidden")
.style("position", "absolute");
// Fetch geojson in the background when component is first mounted.
useEffect(() => {
axios
.get(
`/api/choropleth/geojson?placeDcid=${props.placeInfo.enclosingPlace.dcid}&placeType=${props.placeInfo.enclosedPlaceType}`
)
.then((resp) => {
setGeoJson(resp.data);
setGeoJsonFetched(true);
})
.catch(() => setGeoJsonFetched(true));
}, []);
function replot() {
if (!_.isEmpty(props.points)) {
if (svgContainerRef.current) {
clearSVGs();
plot(svgContainerRef, tooltipRef, mapLegendRef, props, geoJson);
}
}
}
// Replot when props or chart width changes.
useEffect(() => {
const entrySet = new Set();
if (props.display.chartType === ScatterChartType.MAP && !geoJsonFetched) {
loadSpinner(CONTAINER_ID);
return;
} else {
removeSpinner(CONTAINER_ID);
replot();
}
// ResizeObserver callback function documentation:
// https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver
const debouncedHandler = _.debounce((entries) => {
if (_.isEmpty(entries)) return;
if (entrySet.has(entries[0].target)) {
replot();
} else {
entrySet.add(entries[0].target);
}
}, DEBOUNCE_INTERVAL_MS);
const resizeObserver = new ResizeObserver(debouncedHandler);
if (chartContainerRef.current) {
resizeObserver.observe(chartContainerRef.current);
}
return () => {
resizeObserver.unobserve(chartContainerRef.current);
debouncedHandler.cancel();
};
}, [props, chartContainerRef, geoJsonFetched]);
return (
<div id="chart" className="chart-section-container" ref={chartContainerRef}>
<Card className="chart-card">
<div className="chart-title">
<h3>{yTitle}</h3>
<span>vs</span>
<h3>{xTitle}</h3>
</div>
<div className="scatter-chart-container">
<div id={SVG_CONTAINER_ID} ref={svgContainerRef}></div>
<div id={MAP_LEGEND_CONTAINER_ID} ref={mapLegendRef}></div>
<div id="scatter-tooltip" ref={tooltipRef} />
</div>
</Card>
<ToolChartFooter
chartId="scatter"
sources={props.sources}
mMethods={null}
sourceSelectorSvInfoList={props.sourceSelectorSvInfo}
onSvMetahashUpdated={props.onSvMetahashUpdated}
hideIsRatio={true}
>
<PlotOptions />
</ToolChartFooter>
<div id="scatter-chart-screen" className="screen">
<div id="spinner"></div>
</div>
</div>
);
}
Example #10
Source File: plot_options.tsx From website with Apache License 2.0 | 4 votes |
// TODO: Add a new API that given a statvar, a parent place, and a child type,
// returns the available dates for the statvar. Then, fill the datapicker with
// the dates.
function PlotOptions(): JSX.Element {
const { place, x, y, display } = useContext(Context);
const [lowerBound, setLowerBound] = useState(
place.value.lowerBound.toString()
);
const [upperBound, setUpperBound] = useState(
place.value.upperBound.toString()
);
const [xDenomInput, setXDenomInput] = useState(x.value.denom);
const [yDenomInput, setYDenomInput] = useState(y.value.denom);
const yAxisLabel =
display.chartType === ScatterChartType.SCATTER
? "Y-axis"
: y.value.statVarInfo.title || y.value.statVarDcid;
const xAxisLabel =
display.chartType === ScatterChartType.SCATTER
? "X-axis"
: x.value.statVarInfo.title || x.value.statVarDcid;
const axisLabelStyle = {};
if (
yAxisLabel.length > MIN_WIDTH_LABEL_LENGTH ||
xAxisLabel.length > MIN_WIDTH_LABEL_LENGTH
) {
axisLabelStyle["width"] =
Math.min(
MAX_WIDTH_LABEL_LENGTH,
Math.max(xAxisLabel.length, yAxisLabel.length)
) /
2 +
"rem";
}
return (
<Card id="plot-options">
<Container fluid={true}>
<div className="plot-options-row">
<div className="plot-options-label" style={axisLabelStyle}>
{yAxisLabel}:
</div>
<div className="plot-options-input-section">
<div className="plot-options-input">
<FormGroup check>
<Label check>
<Input
id="per-capita-y"
type="checkbox"
checked={y.value.perCapita}
onChange={(e) => y.setPerCapita(e.target.checked)}
/>
Per Capita
</Label>
</FormGroup>
</div>
<div className="plot-options-input">
<FormGroup check>
<Input
id="log-y"
type="checkbox"
checked={y.value.log}
onChange={(e) => checkLog(y, e)}
/>
<Label check>Log scale</Label>
</FormGroup>
</div>
</div>
</div>
<div className="plot-options-row">
<div className="plot-options-label" style={axisLabelStyle}>
{xAxisLabel}:
</div>
<div className="plot-options-input-section">
<div className="plot-options-input">
<FormGroup check>
<Label check>
<Input
id="per-capita-x"
type="checkbox"
checked={x.value.perCapita}
onChange={(e) => x.setPerCapita(e.target.checked)}
/>
Per Capita
</Label>
</FormGroup>
</div>
<div className="plot-options-input">
<FormGroup check>
<Input
id="log-x"
type="checkbox"
checked={x.value.log}
onChange={(e) => checkLog(x, e)}
/>
<Label check>Log scale</Label>
</FormGroup>
</div>
</div>
</div>
{display.chartType === ScatterChartType.SCATTER && (
<>
<div className="plot-options-row">
<div className="plot-options-label">Display:</div>
<div className="plot-options-input-section">
<div className="plot-options-input">
<Button
id="swap-axes"
size="sm"
color="light"
onClick={() => swapAxes(x, y)}
className="plot-options-swap-button"
>
Swap X and Y axes
</Button>
</div>
<div className="plot-options-input">
<FormGroup check>
<Label check>
<Input
id="quadrants"
type="checkbox"
checked={display.showQuadrants}
onChange={(e) => checkQuadrants(display, e)}
/>
Show quadrants
</Label>
</FormGroup>
</div>
<div className="plot-options-input">
<FormGroup check>
<Label check>
<Input
id="quadrants"
type="checkbox"
checked={display.showLabels}
onChange={(e) => checkLabels(display, e)}
/>
Show labels
</Label>
</FormGroup>
</div>
<div className="plot-options-input">
<FormGroup check>
<Label check>
<Input
id="density"
type="checkbox"
checked={display.showDensity}
onChange={(e) => checkDensity(display, e)}
/>
Show density
</Label>
</FormGroup>
</div>
</div>
</div>
<div className="plot-options-row">
<div className="plot-options-label">Filter by population:</div>
<div className="plot-options-input-section pop-filter">
<div className="plot-options-input">
<FormGroup check>
<Input
className="pop-filter-input"
type="number"
onChange={(e) =>
selectLowerBound(place, e, setLowerBound)
}
value={lowerBound}
onBlur={() =>
setLowerBound(place.value.lowerBound.toString())
}
/>
</FormGroup>
</div>
<span>to</span>
<div className="plot-options-input">
<FormGroup check>
<Input
className="pop-filter-input"
type="number"
onChange={(e) =>
selectUpperBound(place, e, setUpperBound)
}
value={upperBound}
onBlur={() =>
setUpperBound(place.value.upperBound.toString())
}
/>
</FormGroup>
</div>
</div>
</div>
</>
)}
</Container>
</Card>
);
}
Example #11
Source File: page.tsx From website with Apache License 2.0 | 4 votes |
render(): JSX.Element {
const numPlaces = Object.keys(this.state.placeName).length;
const numStatVarInfo = Object.keys(this.state.statVarInfo).length;
const namedPlaces: NamedPlace[] = [];
for (const place in this.state.placeName) {
namedPlaces.push({ dcid: place, name: this.state.placeName[place] });
}
const statVarTokens = Array.from(
getTokensFromUrl(TIMELINE_URL_PARAM_KEYS.STAT_VAR, statVarSep)
);
const statVars = statVarTokens.map((sv) =>
sv.includes("|") ? sv.split("|")[0] : sv
);
const deselectSVs = (svList: string[]) => {
const availableSVs = statVars.filter((sv) => svList.indexOf(sv) === -1);
const statVarTokenInfo = {
name: TIMELINE_URL_PARAM_KEYS.STAT_VAR,
sep: statVarSep,
tokens: new Set(availableSVs),
};
setTokensToUrl([statVarTokenInfo]);
};
const svToSvInfo = {};
for (const sv of statVars) {
svToSvInfo[sv] =
sv in this.state.statVarInfo ? this.state.statVarInfo[sv] : {};
}
return (
<>
<StatVarWidget
openSvHierarchyModal={this.state.showSvHierarchyModal}
openSvHierarchyModalCallback={this.toggleSvHierarchyModal}
collapsible={true}
svHierarchyType={StatVarHierarchyType.SCATTER}
samplePlaces={namedPlaces}
deselectSVs={deselectSVs}
selectedSVs={svToSvInfo}
selectSV={(sv) =>
addToken(TIMELINE_URL_PARAM_KEYS.STAT_VAR, statVarSep, sv)
}
/>
<div id="plot-container">
<Container fluid={true}>
{numPlaces === 0 && <h1 className="mb-4">Timelines Explorer</h1>}
<Card id="place-search">
<Row>
<Col sm={12}>
<p>Select places:</p>
</Col>
<Col sm={12}>
<SearchBar
places={this.state.placeName}
addPlace={(place) =>
addToken(TIMELINE_URL_PARAM_KEYS.PLACE, placeSep, place)
}
removePlace={(place) => {
removeToken(
TIMELINE_URL_PARAM_KEYS.PLACE,
placeSep,
place
);
}}
/>
</Col>
</Row>
<Row className="d-lg-none">
<Col>
<Button color="primary" onClick={this.toggleSvHierarchyModal}>
Select variables
</Button>
</Col>
</Row>
</Card>
{numPlaces === 0 && <Info />}
{numPlaces !== 0 && numStatVarInfo !== 0 && (
<div id="chart-region">
<ChartRegion
placeName={this.state.placeName}
statVarInfo={this.state.statVarInfo}
statVarOrder={statVars}
></ChartRegion>
</div>
)}
</Container>
</div>
</>
);
}
Example #12
Source File: ExportDialogButton.tsx From nextclade with MIT License | 4 votes |
export function ExportDialogButton() {
const { t } = useTranslationSafe()
const [isOpen, setIsOpen] = useState<boolean>(false)
const toggleOpen = useCallback(() => setIsOpen((isOpen) => !isOpen), [])
const open = useCallback(() => setIsOpen(true), [])
const close = useCallback(() => setIsOpen(false), [])
const canDownload = useRecoilValue(canDownloadAtom)
// TODO: We could probably use a map and then iterate over it, to reduce duplication
const exportZip = useExportZip()
const exportFasta = useExportFasta()
const exportCsv = useExportCsv()
const exportTsv = useExportTsv()
const exportJson = useExportJson()
const exportNdjson = useExportNdjson()
const exportPeptides = useExportPeptides()
const exportTree = useExportTree()
const exportInsertionsCsv = useExportInsertionsCsv()
const exportErrorsCsv = useExportErrorsCsv()
const exportParams = useMemo(() => DEFAULT_EXPORT_PARAMS, [])
return (
<>
<PanelButton type="button" onClick={open} title={t('Download results')} disabled={!canDownload}>
<DownloadIcon />
</PanelButton>
<Modal centered isOpen={isOpen} toggle={toggleOpen} fade={false} size="lg">
<ModalHeader toggle={close} tag="div" className="d-flex">
<h4 className="mx-auto">
<DownloadIcon />
{t('Download results')}
</h4>
</ModalHeader>
<ModalBody>
<Row>
<Col>
<Card>
<ListGroup flush>
<ExportFileElement
Icon={FileIconJson}
filename={exportParams.filenameJson}
HelpMain={t('Results of the analysis in JSON format.')}
HelpDetails={t(
'Contains detailed results of the analysis, such as clades, mutations, QC metrics etc., in JSON format. Convenient for further automated processing.',
)}
HelpDownload={t('Download results of the analysis in JSON format.')}
onDownload={exportJson}
/>
<ExportFileElement
Icon={FileIconNdjson}
filename={exportParams.filenameNdjson}
HelpMain={t('Results of the analysis in NDJSON format (newline-delimited JSON).')}
HelpDetails={t(
'Contains detailed results of the analysis, such as clades, mutations, QC metrics etc., in NDJSON format. Convenient for further automated processing.',
)}
HelpDownload={t('Download results of the analysis in NDJSON format.')}
onDownload={exportNdjson}
/>
<ExportFileElement
Icon={FileIconCsv}
filename={exportParams.filenameCsv}
HelpMain={t('Summarized results of the analysis in CSV format.')}
HelpDetails={t(
'Contains summarized results of the analysis, such as clades, mutations, QC metrics etc., in tabular format. Convenient for further review and processing using spreadsheets or data-science tools.',
)}
HelpDownload={t('Download summarized results in CSV format')}
onDownload={exportCsv}
/>
<ExportFileElement
Icon={FileIconTsv}
filename={exportParams.filenameTsv}
HelpMain={t('Summarized results of the analysis in TSV format.')}
HelpDetails={t(
'Contains summarized results of the analysis, such as clades, mutations, QC metrics etc in tabular format. Convenient for further review and processing using spreadsheets or data-science tools.',
)}
HelpDownload={t('Download summarized results in TSV format')}
onDownload={exportTsv}
/>
<ExportFileElement
Icon={FileIconJson}
filename={exportParams.filenameTree}
HelpMain={t('Phylogenetic tree with sequenced placed onto it.')}
HelpDetails={
<>
{t('The tree is in Nextstrain format.')}{' '}
{t('Can be viewed locally with Nextstrain Auspice or in {{auspice_us}}')}
<LinkExternal url="https://auspice.us">{'auspice.us'}</LinkExternal>
{'.'}
</>
}
HelpDownload={t(
'Download phylogenetic tree with sequenced placed onto it, in Auspice JSON v2 format.',
)}
onDownload={exportTree}
/>
<ExportFileElement
Icon={FileIconFasta}
filename={exportParams.filenameFasta}
HelpMain={t('Aligned sequences in FASTA format.')}
HelpDetails={t('Contains aligned sequences in FASTA format.')}
HelpDownload={t('Download aligned sequences in FASTA format.')}
onDownload={exportFasta}
/>
<ExportFileElement
Icon={FileIconZip}
filename={exportParams.filenamePeptidesZip}
HelpMain={t('Aligned peptides in FASTA format, zipped')}
HelpDetails={t(
'Contains results of translation of your sequences. One FASTA file per gene, all in a zip archive.',
)}
HelpDownload={t(
'Download aligned peptides in FASTA format, one file per gene, all in a zip archive.',
)}
onDownload={exportPeptides}
/>
<ExportFileElement
Icon={FileIconCsv}
filename={exportParams.filenameInsertionsCsv}
HelpMain={t('Insertions in CSV format.')}
HelpDetails={t('Contains insertions stripped from aligned sequences.')}
HelpDownload={t('Download insertions in CSV format')}
onDownload={exportInsertionsCsv}
/>
<ExportFileElement
Icon={FileIconCsv}
filename={exportParams.filenameErrorsCsv}
HelpMain={t('Errors, warnings, and failed genes in CSV format.')}
HelpDetails={t(
'Contains a list of errors, a list of warnings and a list of genes that failed processing, per sequence, in CSV format.',
)}
HelpDownload={t('Download warnings, and failed genes in CSV format')}
onDownload={exportErrorsCsv}
/>
<ExportFileElement
Icon={FileIconZip}
filename={exportParams.filenameZip}
HelpMain={t('All files in a zip archive.')}
HelpDetails={t('Contains all of the above files in a single zip file.')}
HelpDownload={t('Download all in zip archive')}
onDownload={exportZip}
/>
</ListGroup>
</Card>
</Col>
</Row>
</ModalBody>
<ModalFooter>
<Button type="button" onClick={close} title={t('Close')}>
<div>{t('Close')}</div>
</Button>
</ModalFooter>
</Modal>
</>
)
}
Example #13
Source File: CovidCard.tsx From health-cards-tests with MIT License | 4 votes |
CovidCard: React.FC<{
holderState: HolderState,
smartState: SmartState,
uiState: UiState,
displayQr: (vc) => Promise<void>,
openScannerUi: () => Promise<void>,
connectToIssuer: () => Promise<void>,
connectToFhir: () => Promise<void>,
dispatchToHolder: (e: Promise<any>) => Promise<void>
}> = ({ holderState, smartState, uiState, displayQr, openScannerUi, connectToFhir, connectToIssuer, dispatchToHolder }) => {
const issuerInteractions = holderState.interactions.filter(i => i.siopPartnerRole === 'issuer').slice(-1)
const issuerInteraction = issuerInteractions.length ? issuerInteractions[0] : null
let currentStep = CardStep.CONFIGURE_WALLET;
/* tslint:disable-next-line:prefer-conditional-expression */
if (issuerInteraction?.status !== 'complete') {
currentStep = CardStep.CONNECT_TO_ISSUER;
} else {
currentStep = CardStep.DOWNLOAD_CREDENTIAL;
}
if (holderState.vcStore.length) {
currentStep = CardStep.COMPLETE
}
const retrieveVcClick = async () => {
const onMessage = async ({ data, source }) => {
const { verifiableCredential } = data
window.removeEventListener("message", onMessage)
await dispatchToHolder(receiveVcs(verifiableCredential, holderState))
}
window.addEventListener("message", onMessage)
window.open(uiState.issuer.issuerDownloadUrl)
}
useEffect(() => {
if (smartState?.access_token && holderState.vcStore.length === 0) {
const credentials = axios.post(uiState.fhirClient.server + `/Patient/${smartState.patient}/$health-cards-issue`, {
"resourceType": "Parameters",
"parameter": [{
"name": "credentialType",
"valueUri": "https://smarthealth.cards#immunization"
},{
"name": "presentationContext",
"valueUri": "https://smarthealth.cards#presentation-context-online"
}, {
"name": "encryptForKeyId",
"valueString": "#encryption-key-1"
}]
})
credentials.then(response => {
const vcs = response.data.parameter.filter(p => p.name === 'verifiableCredential').map(p => p.valueString)
dispatchToHolder(receiveVcs(vcs, holderState))
})
}
}, [smartState])
const covidVcs = holderState.vcStore.filter(vc => vc.type.includes("https://smarthealth.cards#covid19"));
const resources = covidVcs.flatMap(vc =>
vc.vcPayload.vc.credentialSubject.fhirBundle.entry
.flatMap(e => e.resource))
const doses = resources.filter(r => r.resourceType === 'Immunization').length;
const patient = resources.filter(r => r.resourceType === 'Patient')[0];
console.log("P", patient);
useEffect(() => {
if (covidVcs.length === 0) {
return;
}
let file = new File([JSON.stringify({
"verifiableCredential": covidVcs.map(v => v.vcSigned)
})], "c19.smart-health-card", {
type: "application/smart-health-card"
})
const url = window.URL.createObjectURL(file);
setDownloadFileUrl(url)
}, [covidVcs.length])
const [downloadFileUrl, setDownloadFileUrl] = useState("");
return <> {
currentStep === CardStep.COMPLETE && <Card style={{ border: "1px solid grey", padding: ".5em", marginBottom: "1em" }}>
<CardTitle style={{ fontWeight: "bolder" }}>
COVID Cards ({covidVcs.length})
</CardTitle>
<CardSubtitle className="text-muted">Your COVID results are ready to share, based on {" "}
{resources && <>{resources.length} FHIR Resource{resources.length > 1 ? "s" : ""} <br /> </>}
</CardSubtitle>
<ul>
<li> Name: {patient.name[0].given[0]} {patient.name[0].family}</li>
<li> Birthdate: {patient.birthDate}</li>
<li> Immunization doses received: {doses}</li>
</ul>
<Button className="mb-1" color="info" onClick={() => displayQr(covidVcs[0])}>Display QR</Button>
<a href={downloadFileUrl} download="covid19.smart-health-card">Download file</a>
</Card>
} {currentStep < CardStep.COMPLETE &&
<Card style={{ border: ".25em dashed grey", padding: ".5em", marginBottom: "1em" }}>
<CardTitle>COVID Cards </CardTitle>
<CardSubtitle className="text-muted">You don't have any COVID cards in your wallet yet.</CardSubtitle>
<Button disabled={true} className="mb-1" color="info">
{currentStep > CardStep.CONFIGURE_WALLET && '✓ '} 1. Set up your Health Wallet</Button>
<RS.UncontrolledButtonDropdown className="mb-1" >
<DropdownToggle caret color={currentStep === CardStep.CONNECT_TO_ISSUER ? 'success' : 'info'} >
{currentStep > CardStep.CONNECT_TO_ISSUER && '✓ '}
2. Get your Vaccination Credential
</DropdownToggle>
<DropdownMenu style={{ width: "100%" }}>
<DropdownItem onClick={connectToFhir} >Connect with SMART on FHIR </DropdownItem>
<DropdownItem >Load from file (todo)</DropdownItem>
</DropdownMenu>
</RS.UncontrolledButtonDropdown>
<Button
disabled={currentStep !== CardStep.DOWNLOAD_CREDENTIAL}
onClick={retrieveVcClick}
className="mb-1"
color={currentStep === CardStep.DOWNLOAD_CREDENTIAL ? 'success' : 'info'} >
3. Save COVID card to wallet</Button>
</Card>
}
</>
}
Example #14
Source File: holder-page.tsx From health-cards-tests with MIT License | 4 votes |
App: React.FC<AppProps> = (props) => {
const [holderState, setHolderState] = useState<HolderState>(props.initialHolderState)
const [uiState, dispatch] = useReducer(uiReducer, props.initialUiState)
const [smartState, setSmartState] = useState<SmartState | null>(null)
const issuerInteractions = holderState.interactions.filter(i => i.siopPartnerRole === 'issuer').slice(-1)
const verifierInteractions = holderState.interactions.filter(i => i.siopPartnerRole === 'verifier').slice(-1)
const siopAtNeedQr = issuerInteractions.concat(verifierInteractions).filter(i => i.status === 'need-qrcode').slice(-1)
useEffect(() => {
holderState.interactions.filter(i => i.status === 'need-redirect').forEach(i => {
const redirectUrl = i.siopRequest.client_id + '#' + qs.encode(i.siopResponse.formPostBody)
const opened = window.open(redirectUrl, "_blank")
dispatchToHolder({ 'type': "siop-response-complete" })
})
}, [holderState.interactions])
const dispatchToHolder = async (ePromise) => {
const e = await ePromise
const holder = await holderReducer(holderState, e)
setHolderState(state => holder)
console.log("After event", e, "Holder state is", holder)
}
const connectTo = who => async () => {
dispatchToHolder({ 'type': 'begin-interaction', who })
}
const onScanned = async (qrCodeUrl: string) => {
dispatch({ type: 'scan-barcode' })
await dispatchToHolder(receiveSiopRequest(qrCodeUrl, holderState));
}
const connectToFhir = async () => {
const connected = await makeFhirConnector(uiState, holderState)
setSmartState(connected.newSmartState)
}
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
return <div style={{ paddingTop: "5em" }}>
<RS.Navbar expand="" className="navbar-dark bg-info fixed-top">
<RS.Container>
<NavbarBrand style={{ marginRight: "2em" }} href="/">
<img className="d-inline-block" style={{ maxHeight: "1em", maxWidth: "1em", marginRight: "10px" }} src={logo} />
Health Wallet Demo
</NavbarBrand>
<NavbarToggler onClick={toggle} />
<Collapse navbar={true} isOpen={isOpen}>
<Nav navbar={true}>
<NavLink href="#" onClick={() => {
dispatch({ type: 'open-scanner', 'label': 'Verifier' })
}}>Scan QR to Share</NavLink>
<NavLink href="#" onClick={connectTo('verifier')}> Open Employer Portal</NavLink>
<NavLink href="#config" onClick={e => dispatch({ type: 'toggle-editing-config' })}> Edit Config</NavLink>
<NavLink target="_blank" href="https://github.com/microsoft-healthcare-madison/health-wallet-demo">Source on GitHub</NavLink>
</Nav>
</Collapse></RS.Container>
</RS.Navbar>
{uiState.scanningBarcode?.active &&
<SiopRequestReceiver
onReady={onScanned}
onCancel={() => dispatch({'type': 'close-scanner'})}
redirectMode="qr"
label={uiState.scanningBarcode?.label}
/>
}
{siopAtNeedQr.length > 0 &&
<SiopRequestReceiver
onReady={onScanned}
onCancel={() => dispatch({'type': 'close-scanner'})}
redirectMode="window-open"
label={siopAtNeedQr[0].siopPartnerRole}
startUrl={siopAtNeedQr[0].siopPartnerRole === 'issuer' ? uiState.issuer.issuerStartUrl : uiState.verifier.verifierStartUrl}
/>}
{uiState.editingConfig && <ConfigEditModal uiState={uiState} defaultUiState={props.defaultUiState} dispatch={dispatch} />}
{uiState.presentingQr?.active && <QRPresentationModal healthCard={uiState.presentingQr.vcToPresent} dispatch={dispatch} />}
<SiopApprovalModal {...parseSiopApprovalProps(holderState, dispatchToHolder)} />
<RS.Container >
<RS.Row>
<RS.Col xs="12">
<CovidCard
holderState={holderState}
smartState={smartState}
uiState={uiState}
displayQr={async (vc) => {
dispatch({type: 'begin-qr-presentation', vc})
}}
openScannerUi={async () => {
dispatch({ type: 'open-scanner', 'label': 'Lab' })
}}
connectToIssuer={connectTo('issuer')}
connectToFhir={connectToFhir}
dispatchToHolder={dispatchToHolder}
/>
<Card style={{ padding: ".5em" }}>
<CardTitle style={{ fontWeight: "bolder" }}>
Debugging Details
</CardTitle>
<CardSubtitle className="text-muted">Your browser's dev tools will show details on current page state + events </CardSubtitle>
</Card>
</RS.Col>
</RS.Row>
</RS.Container>
<div>
</div>
</div>
}