Skip to content
Draft
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
9 changes: 9 additions & 0 deletions app/src/components/DateProgressBar/i18n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"namespace": "dateProgressBar",
"strings": {
"startDateLabel": "Start",
"endDateLabel": "End",
"operationTimelineLabel": "Operation Timeline",
"imminentDrefLabel": "Imminent DREF"
}
}
121 changes: 121 additions & 0 deletions app/src/components/DateProgressBar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useMemo } from 'react';
import { ChartPoint } from '@ifrc-go/ui';
import { useTranslation } from '@ifrc-go/ui/hooks';

import i18n from './i18n.json';
import styles from './styles.module.css';

interface Props {
startDate: string;
endDate: string;
}

function DateProgressBar(props: Props) {
const {
startDate,
endDate,
} = props;
const strings = useTranslation(i18n);

const {
progress,
isActive,
startDateValue,
endDateValue,
} = useMemo(() => {
const start = new Date(startDate);
const end = new Date(endDate);
const today = new Date();

const startTime = start.getTime();
const endTime = end.getTime();
const todayTime = today.getTime();

const totalSpan = endTime - startTime;
const val = totalSpan > 0 ? ((todayTime - startTime) / totalSpan) * 100 : 0;

return {
progress: Math.min(100, Math.max(0, val)),
isActive: todayTime >= startTime && todayTime <= endTime,
startDateValue: start.toLocaleDateString(),
endDateValue: end.toLocaleDateString(),
};
}, [startDate, endDate]);

const width = 200;
const height = 20;
const centerY = height / 2;
const progressX = (progress / 100) * width;

return (
<div className={styles.container}>
<div className={styles.timelineWrapper}>
<div className={styles.titleStart}>
{strings.operationTimelineLabel}
</div>
<div className={styles.titleEnd}>
{strings.imminentDrefLabel}
</div>
<svg
className={styles.svg}
viewBox={`0 0 ${width} ${height}`}
>
<line
className={styles.dashedSeparator}
x1={0}
y1={0}
x2={0}
y2={height}
/>
<line
className={styles.dashedSeparator}
x1={width}
y1={0}
x2={width}
y2={height}
/>
<line
className={styles.track}
x1={0}
y1={centerY}
x2={width}
y2={centerY}
/>
<line
className={styles.progress}
x1={0}
y1={centerY}
x2={progressX}
y2={centerY}
/>
{isActive && (
<ChartPoint
className={styles.pointOutline}
x={progressX}
y={centerY}
/>
)}
</svg>
<div className={styles.labelStart}>
<div className={styles.subLabel}>
{strings.startDateLabel}
</div>
<div className={styles.dateText}>
{startDateValue}
</div>
</div>

<div className={styles.labelEnd}>
<div className={styles.subLabel}>
{strings.endDateLabel}
</div>
<div className={styles.dateText}>
{endDateValue}
</div>
</div>
</div>
</div>
);
}

export default DateProgressBar;
58 changes: 58 additions & 0 deletions app/src/components/DateProgressBar/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
.container {
background-color: var(--go-ui-color-white);
padding: var(--go-ui-spacing-xl);
width: auto;

.timeline-wrapper {
position: relative;
padding: var(--go-ui-spacing-xl) 0;
width: 100%;

.svg {
display: block;
width: 100%;
overflow: visible;
}

.track {
stroke: var(--go-ui-color-separator);
stroke-width: 2;
}

.progress {
stroke: var(--go-ui-color-primary-red);
stroke-width: 3;
stroke-linecap: round;
}

.dashed-separator {
stroke: var(--go-ui-color-separator);
stroke-width: 1;
stroke-dasharray: 4 2;
}

.point-outline {
color: var(--go-ui-color-primary-red);
}

.title-start, .label-start {
position: absolute;
white-space: nowrap;
}

.title-end, .label-end {
position: absolute;
right: 0;
text-align: right;
white-space: nowrap;
}

.title-start, .title-end {
top: 0;
}

.label-start, .label-end {
bottom: 0;
}
}
}
9 changes: 9 additions & 0 deletions app/src/components/TimelineBar/i18n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"namespace": "timelineBar",
"strings": {
"startDateLabel": "Start of Imminent Ops",
"todayLabel": "Today",
"forecastLabel": "Forecast",
"operationEnd": "End of the Operation"
}
}
104 changes: 104 additions & 0 deletions app/src/components/TimelineBar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {
Container,
TextOutput,
} from '@ifrc-go/ui';
import { useTranslation } from '@ifrc-go/ui/hooks';
import {
formatDate,
getNumberOfDays,
} from '@ifrc-go/ui/utils';

import i18n from './i18n.json';
import styles from './styles.module.css';

interface Props {
imminentStartDate: string | Date;
operationEndDate: string | Date;
forecastDate: string | Date;
heading: string;
}

function TimelineBar(props: Props) {
const {
imminentStartDate,
operationEndDate,
forecastDate,
heading,
} = props;

const strings = useTranslation(i18n);
const start = new Date(imminentStartDate);
const end = new Date(operationEndDate);
const fDate = new Date(forecastDate);
const today = new Date();

const totalDuration = getNumberOfDays(start, end) || 1;

const getPosition = (targetDate: Date) => {
const daysFromStart = getNumberOfDays(start, targetDate) ?? 0;
const percentage = (daysFromStart / totalDuration) * 100;
return Math.min(Math.max(percentage, 0), 100);
};

return (
<Container
heading={heading}
withHeaderBorder
>
<div className={styles.timelineContainer}>
<div className={styles.mainLine} />
<div
className={styles.todayIndicator}
style={{ left: `${getPosition(today)}%` }}
>
<span className={styles.todayLabel}>
{strings.todayLabel}
</span>
</div>

<div
className={styles.startMilestone}
style={{ left: '1%' }}
>
<TextOutput
labelClassName={styles.redLabel}
label={strings.startDateLabel}
value={formatDate(start)}
withoutLabelColon
strongLabel
/>
<div className={styles.markerTop} />
</div>

<div
className={styles.forecastMilestone}
style={{ left: `${getPosition(fDate)}%` }}
>
<div className={styles.markerBottom} />
<TextOutput
label={strings.forecastLabel}
value={formatDate(fDate)}
withoutLabelColon
strongLabel
/>
</div>

<div
className={styles.endMilestone}
style={{ right: '1%' }}
>
<TextOutput
labelClassName={styles.redLabel}
label={strings.operationEnd}
value={formatDate(end)}
withoutLabelColon
strongLabel
/>
<div className={styles.markerTop} />
</div>
</div>
</Container>
);
}

export default TimelineBar;
65 changes: 65 additions & 0 deletions app/src/components/TimelineBar/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
.timeline-container {
position: relative;
box-shadow: var(--go-ui-box-shadow-xs);
width: 100%;
height: 20rem;

.main-line {
position: absolute;
top: 50%;
z-index: 1;
background-color: var(--go-ui-color-black);
width: 100%;
height: var(--go-ui-width-separator-thin);
}

.today-indicator {
position: absolute;
top: 10%;
bottom: 10%;
z-index: 2;
background-color: var(--go-ui-color-black);
width: var(--go-ui-width-separator-thin);

.today-label {
position: absolute;
top: -25px;
left: 50%;
transform: translateX(-50%);
font-weight: var(--go-ui-font-weight-medium);
}
}

/* NOTE: Only camelCase is working here */
.startMilestone, .endMilestone, .forecastMilestone {
Comment on lines +33 to +34
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to look into this, and we can name like milestone and use in all 3 conditions

display: flex;
position: absolute;
flex-direction: column;
z-index: 3;
}

.startMilestone, .endMilestone {
bottom: 50%;
align-items: flex-start;
}

.endMilestone {
align-items: flex-end;
}

.forecastMilestone {
top: 50%;
align-items: flex-start;
}

.marker-top, .marker-bottom {
background-color: var(--go-ui-color-black);
width: var(--go-ui-width-separator-thin);
height: 5rem;
}

.red-label {
color: var(--go-ui-color-red);
font-weight: var(--go-ui-font-weight-semibold);
}
}
4 changes: 3 additions & 1 deletion app/src/views/Emergency/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"emergencies": "Emergencies",
"emergencyEdit": "Edit Event",
"emergencyFollow":"Follow",
"emergencyUnfollow":"Unfollow"
"emergencyUnfollow":"Unfollow",
"fundingRequirements": "Funding Requirements (CHF)",
"targetedPopulation": "Targeted Population"
}
}
Loading
Loading