diff --git a/app/src/components/DateProgressBar/i18n.json b/app/src/components/DateProgressBar/i18n.json
new file mode 100644
index 0000000000..03fdccac7b
--- /dev/null
+++ b/app/src/components/DateProgressBar/i18n.json
@@ -0,0 +1,9 @@
+{
+ "namespace": "dateProgressBar",
+ "strings": {
+ "startDateLabel": "Start",
+ "endDateLabel": "End",
+ "operationTimelineLabel": "Operation Timeline",
+ "imminentDrefLabel": "Imminent DREF"
+ }
+}
\ No newline at end of file
diff --git a/app/src/components/DateProgressBar/index.tsx b/app/src/components/DateProgressBar/index.tsx
new file mode 100644
index 0000000000..b3df2f1cec
--- /dev/null
+++ b/app/src/components/DateProgressBar/index.tsx
@@ -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 (
+
+
+
+ {strings.operationTimelineLabel}
+
+
+ {strings.imminentDrefLabel}
+
+
+
+
+ {strings.startDateLabel}
+
+
+ {startDateValue}
+
+
+
+
+
+ {strings.endDateLabel}
+
+
+ {endDateValue}
+
+
+
+
+ );
+}
+
+export default DateProgressBar;
diff --git a/app/src/components/DateProgressBar/styles.module.css b/app/src/components/DateProgressBar/styles.module.css
new file mode 100644
index 0000000000..16bd47ebf5
--- /dev/null
+++ b/app/src/components/DateProgressBar/styles.module.css
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/components/TimelineBar/i18n.json b/app/src/components/TimelineBar/i18n.json
new file mode 100644
index 0000000000..9a56fcd00b
--- /dev/null
+++ b/app/src/components/TimelineBar/i18n.json
@@ -0,0 +1,9 @@
+{
+ "namespace": "timelineBar",
+ "strings": {
+ "startDateLabel": "Start of Imminent Ops",
+ "todayLabel": "Today",
+ "forecastLabel": "Forecast",
+ "operationEnd": "End of the Operation"
+ }
+}
\ No newline at end of file
diff --git a/app/src/components/TimelineBar/index.tsx b/app/src/components/TimelineBar/index.tsx
new file mode 100644
index 0000000000..ca35813d94
--- /dev/null
+++ b/app/src/components/TimelineBar/index.tsx
@@ -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 (
+
+
+
+
+
+ {strings.todayLabel}
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default TimelineBar;
diff --git a/app/src/components/TimelineBar/styles.module.css b/app/src/components/TimelineBar/styles.module.css
new file mode 100644
index 0000000000..e3fe561add
--- /dev/null
+++ b/app/src/components/TimelineBar/styles.module.css
@@ -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 {
+ 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);
+ }
+}
diff --git a/app/src/views/Emergency/i18n.json b/app/src/views/Emergency/i18n.json
index 0e4d37c8c1..6437836e9d 100644
--- a/app/src/views/Emergency/i18n.json
+++ b/app/src/views/Emergency/i18n.json
@@ -14,6 +14,8 @@
"emergencies": "Emergencies",
"emergencyEdit": "Edit Event",
"emergencyFollow":"Follow",
- "emergencyUnfollow":"Unfollow"
+ "emergencyUnfollow":"Unfollow",
+ "fundingRequirements": "Funding Requirements (CHF)",
+ "targetedPopulation": "Targeted Population"
}
}
diff --git a/app/src/views/Emergency/index.tsx b/app/src/views/Emergency/index.tsx
index 8be8a851fa..653d4efb21 100644
--- a/app/src/views/Emergency/index.tsx
+++ b/app/src/views/Emergency/index.tsx
@@ -16,8 +16,12 @@ import {
Breadcrumbs,
Button,
KeyFigureView,
+ Label,
ListView,
NavigationTabList,
+ NumberOutput,
+ ProgressBar,
+ TextOutput,
} from '@ifrc-go/ui';
import { useTranslation } from '@ifrc-go/ui/hooks';
import {
@@ -30,6 +34,7 @@ import {
listToMap,
} from '@togglecorp/fujs';
+import DateProgressBar from '#components/DateProgressBar';
import Link from '#components/Link';
import NavigationTab from '#components/NavigationTab';
import Page from '#components/Page';
@@ -274,26 +279,74 @@ export function Component() {
)}
heading={emergencyResponse?.name ?? '--'}
description={(
- <>
-
+
- {region?.region_name}
-
-
+ {region?.region_name}
+
+
+ {country?.name}
+
+
+
- {country?.name}
-
- >
+ {/* TODO: Add the value here */}
+
+ {/* TODO: Add the value here */}
+
+
+ {/* TODO: Add the value here */}
+
+
+ )}
+ />
+ {/* TODO: Add the value here */}
+
+
+
)}
info={(