commit 6c66dc8a7c14a41bc6ea3e841ca4dc09a0549041
parent 78612fe68242d0e9ae9348f14933faf91a7b4f11
Author: Agastya Chandrakant <acagastya@outlook.com>
Date: Mon, 25 May 2020 21:36:16 +0530
Add Future; saving pending
Diffstat:
5 files changed, 265 insertions(+), 3 deletions(-)
diff --git a/src/App/comps/ErrorAlert.tsx b/src/App/comps/ErrorAlert.tsx
@@ -0,0 +1,22 @@
+import React from 'react';
+
+function ErrorAlert({ msg }: { msg: string }) {
+ return (
+ <div
+ className="alert alert-warning alert-dismissible fade show mt-5"
+ role="alert"
+ >
+ {msg}
+ <button
+ type="button"
+ className="close"
+ data-dismiss="alert"
+ aria-label="Close"
+ >
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ );
+}
+
+export default ErrorAlert;
diff --git a/src/App/index.tsx b/src/App/index.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';
import DualConvertor from './pages/DualConvertor';
+import FutureConversion from './pages/FutureConversion';
import Help from './pages/Help';
import SimpleConvertor from './pages/SimpleConvertor';
@@ -24,6 +25,18 @@ function App<never>(): JSX.Element {
>
<Header />
<Switch>
+ <Route
+ path="/future"
+ render={() => (
+ <FutureConversion
+ time={now}
+ setTZ1={setTimezone1}
+ setTZ2={setTimezone2}
+ TZ1={timezone1}
+ TZ2={timezone2}
+ />
+ )}
+ />
<Route path="/help" render={() => <Help time={now} />} />
<Route
path="/from-to"
diff --git a/src/App/pages/FutureConversion.tsx b/src/App/pages/FutureConversion.tsx
@@ -0,0 +1,206 @@
+import React from 'react';
+import moment from 'moment-timezone';
+
+import TimezoneInput from '../comps/TimezoneInput';
+
+import {
+ displayTime,
+ friendlyStr,
+ getAbbr,
+ timezoneList,
+ HMMDY,
+ HMSDMY,
+} from '../../utils';
+import { HM, MAX_DATE, YMD, localTimezone } from '../../utils';
+import { IFutureConversion, IFutureConverted } from '../../utils/interfaces';
+import ErrorAlert from '../comps/ErrorAlert';
+
+function FutureConversion({
+ time: now,
+ setTZ1,
+ setTZ2,
+ TZ1,
+ TZ2,
+}: IFutureConversion): JSX.Element {
+ //
+ const [date, setDate] = React.useState(
+ displayTime({ fmtStr: YMD, time: now, timezone: localTimezone })
+ );
+ const [time, setTime] = React.useState(
+ displayTime({ fmtStr: HM, time: now, timezone: localTimezone })
+ );
+
+ const [selectedTime, setSelectedTime] = React.useState<
+ moment.Moment | undefined
+ >();
+ const [err, setErr] = React.useState<string>('');
+
+ function handleDateChange(e: React.ChangeEvent<HTMLInputElement>): void {
+ setDate(e.target.value);
+ setSelectedTime(undefined);
+ }
+
+ function handleFormSubmit(e: React.FormEvent<HTMLFormElement>): void {
+ e.preventDefault();
+
+ // 0. Reset Error and selected time
+ setErr('');
+ setSelectedTime(undefined);
+ // 1. Validate selected date
+ const selectedDate = moment(date);
+ if (!selectedDate.isValid()) {
+ setErr('Chosen date is not valid.');
+ console.warn(err);
+ return;
+ }
+ // 2. Check limit of selected date
+ // 2.1 MAX allowed
+ const epoch = moment(MAX_DATE, YMD);
+ if (epoch.unix() - selectedDate.unix() < 0) {
+ setErr('Chosen date is outside the maximum permissible limit.');
+ console.warn(err);
+ return;
+ }
+ // 2.2 MIN allowed
+ const todayMoment = moment(now.format(YMD), YMD);
+ const selectedMoment = moment(selectedDate.format(YMD), YMD);
+ if (selectedMoment.unix() - todayMoment.unix() < 0) {
+ setErr('Chosen date is in the past.');
+ console.warn(err);
+ return;
+ }
+ // 3. Validate chosen time
+ const dateStr = selectedDate.format(YMD);
+ const timeStr = time;
+ const dateTimeStr = `${dateStr} ${timeStr}`;
+ const dateTime = moment.tz(dateTimeStr, TZ1);
+ if (!dateTime.isValid()) {
+ setErr('Error occurred while parsing time.');
+ console.warn(err);
+ return;
+ }
+ // 4. validate chosen timezone
+ if (timezoneList.indexOf(TZ1) < 0) {
+ setErr('Selected timezone is invalid.');
+ console.warn(err);
+ return;
+ }
+ // 5. validate set timezone
+ if (timezoneList.indexOf(TZ2) < 0) {
+ setErr('Selected timezone to convert is invalid.');
+ console.warn(err);
+ return;
+ }
+ // 6. convert time
+ setSelectedTime(dateTime);
+ }
+
+ function handleTimeChange(e: React.ChangeEvent<HTMLInputElement>): void {
+ setTime(e.target.value);
+ setSelectedTime(undefined);
+ }
+
+ return (
+ <div className="container">
+ <form onSubmit={handleFormSubmit}>
+ <div className="form-group">
+ <label htmlFor="choose-date">Choose date</label>
+ <input
+ aria-describedby="choose-date"
+ className="form-control"
+ id="chosen-date"
+ max={MAX_DATE}
+ min={displayTime({
+ fmtStr: YMD,
+ time: now,
+ timezone: localTimezone,
+ })}
+ name="choose-date"
+ onChange={handleDateChange}
+ placeholder="Enter date"
+ type="date"
+ value={date}
+ />
+ </div>
+ <div className="form-group">
+ <label htmlFor="choose-time">Choose time</label>
+ <input
+ aria-describedby="choose-time"
+ className="form-control"
+ id="choose-time"
+ name="choose-time"
+ onChange={handleTimeChange}
+ placeholder="Enter time"
+ type="time"
+ value={time}
+ />
+ </div>
+ <div className="form-group">
+ <label htmlFor="choose-from-timezone">Set timezone</label>
+ <TimezoneInput
+ autofocus={false}
+ changeValue={setTZ1}
+ clearInput={setSelectedTime}
+ TZ={TZ1}
+ id="choose-from-timezone"
+ placeholder="Set timezone"
+ />
+ </div>
+ <div className="form-group">
+ <label htmlFor="convert-to-timezone">Convert to timezone</label>
+ <TimezoneInput
+ autofocus={false}
+ changeValue={setTZ2}
+ clearInput={setSelectedTime}
+ TZ={TZ2}
+ id="convert-to-timezone"
+ placeholder="Convert to timezone"
+ />
+ </div>
+ <button type="submit" className="btn btn-success">
+ Convert
+ </button>
+ {err ? <ErrorAlert msg={err} /> : null}
+ {selectedTime !== undefined ? (
+ <FutureConverted selectedTime={selectedTime} TZ1={TZ1} TZ2={TZ2} />
+ ) : null}
+ </form>
+ </div>
+ );
+}
+
+function FutureConverted({
+ selectedTime,
+ TZ1,
+ TZ2,
+}: IFutureConverted): JSX.Element {
+ // console.log(selectedTime);
+ const chosenTime = selectedTime;
+ const convertedTime = selectedTime.clone().tz(TZ2);
+ const fromLbl = getAbbr({ timezone: TZ1, time: moment(chosenTime, HMSDMY) });
+ const toLbl = getAbbr({ timezone: TZ2, time: moment(convertedTime, HMSDMY) });
+ return (
+ <div className="mt-5">
+ <div className="row">
+ <div className="col">
+ <h1>{friendlyStr(TZ1)}</h1>
+ </div>
+ <div className="col">
+ <h1>{friendlyStr(TZ2)}</h1>
+ </div>
+ </div>
+ <div className="row">
+ <div className="col">
+ {displayTime({ fmtStr: HMMDY, time: chosenTime, timezone: TZ1 })} (
+ {fromLbl})
+ </div>
+ <div className="col">
+ {displayTime({ fmtStr: HMMDY, time: convertedTime, timezone: TZ2 })} (
+ {toLbl})
+ </div>
+ </div>
+ </div>
+ );
+}
+
+export default FutureConversion;
diff --git a/src/utils/index.ts b/src/utils/index.ts
@@ -37,7 +37,7 @@ function friendlyStr(str: string): string {
*/
function getAbbr({ timezone, time }: { timezone: string, time: moment.Moment }): string {
// @ts-ignore
- return moment.tz.zone(timezone).abbr(time.unix());
+ return moment.tz.zone(timezone).abbr(time);
}
/**
@@ -103,15 +103,23 @@ export {
};
const DEFAULT_TZ = 'UTC';
-const HMSDMY = 'HH:mm:ss MMMM, DD, YYYY';
+const HM = 'HH:mm';
+const HMMDY = 'HH:mm MMMM DD, YYYY';
+const HMSDMY = 'HH:mm:ss MMMM DD, YYYY';
const localTimezone = moment.tz.guess();
+const MAX_DATE = '2038-01-18';
const timezoneList = moment.tz.names().sort();
const YEAR = new Date().getFullYear();
+const YMD = 'YYYY-MM-DD'
export {
DEFAULT_TZ,
+ HM,
+ HMMDY,
HMSDMY,
localTimezone,
+ MAX_DATE,
timezoneList,
- YEAR
+ YEAR,
+ YMD
};
\ No newline at end of file
diff --git a/src/utils/interfaces.tsx b/src/utils/interfaces.tsx
@@ -14,6 +14,19 @@ export interface IDisplayTime {
timezone: string;
}
+export interface IFutureConversion {
+ time: moment.Moment;
+ setTZ1: React.Dispatch<React.SetStateAction<string>>;
+ setTZ2: React.Dispatch<React.SetStateAction<string>>;
+ TZ1: string;
+ TZ2: string;
+}
+export interface IFutureConverted {
+ selectedTime: moment.Moment;
+ TZ1: string;
+ TZ2: string;
+}
+
export interface IMoment {
time: moment.Moment;
}