195 lines
9.1 KiB
TypeScript
195 lines
9.1 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import classNames from 'classnames';
|
||
import { format } from 'date-fns';
|
||
import { CheckBox } from './CheckBox';
|
||
import { FieldError, StandaloneFieldError } from './FieldError';
|
||
import { TagInput } from './TagInput';
|
||
import { MetadataPreview } from './MetadataPreview';
|
||
|
||
type CheckinFormProps = {
|
||
initialState: any;
|
||
};
|
||
|
||
export const CheckinForm: React.FC<CheckinFormProps> = ({ initialState }) => {
|
||
const mode = initialState.mode;
|
||
const [date, setDate] = useState<string>(initialState.fields.date || '');
|
||
const [time, setTime] = useState<string>(initialState.fields.time || '');
|
||
const [tags, setTags] = useState<string[]>(initialState.fields.tags || []);
|
||
const [link, setLink] = useState<string>(initialState.fields.link || '');
|
||
const [linkForPreview, setLinkForPreview] = useState(link);
|
||
const [note, setNote] = useState<string>(initialState.fields.note || '');
|
||
const [isRealtime, setRealtime] = useState<boolean>(mode === 'create' && initialState.fields.is_realtime);
|
||
const [isPrivate, setPrivate] = useState<boolean>(!!initialState.fields.is_private);
|
||
const [isTooSensitive, setTooSensitive] = useState<boolean>(!!initialState.fields.is_too_sensitive);
|
||
const [discardElapsedTime, setDiscardElapsedTime] = useState<boolean>(!!initialState.fields.discard_elapsed_time);
|
||
useEffect(() => {
|
||
if (mode === 'create' && isRealtime) {
|
||
const id = setInterval(() => {
|
||
const now = new Date();
|
||
setDate(format(now, 'yyyy/MM/dd'));
|
||
setTime(format(now, 'HH:mm'));
|
||
}, 500);
|
||
return () => clearInterval(id);
|
||
}
|
||
}, [mode, isRealtime]);
|
||
|
||
return (
|
||
<>
|
||
<div className="form-row">
|
||
{mode === 'create' && (
|
||
<div className="col-sm-12 mb-2">
|
||
<CheckBox
|
||
id="isRealtime"
|
||
name="is_realtime"
|
||
checked={isRealtime}
|
||
onChange={(v) => setRealtime(v)}
|
||
>
|
||
現在時刻でチェックイン
|
||
</CheckBox>
|
||
</div>
|
||
)}
|
||
<div className="form-group col-sm-6">
|
||
<label htmlFor="date">
|
||
<span className="oi oi-calendar" /> 日付
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="date"
|
||
name="date"
|
||
className={classNames({
|
||
'form-control': true,
|
||
'is-invalid': initialState.errors?.date || initialState.errors?.datetime,
|
||
})}
|
||
pattern="^20[0-9]{2}/(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])$"
|
||
required
|
||
value={date}
|
||
onChange={(e) => setDate(e.target.value)}
|
||
disabled={isRealtime}
|
||
/>
|
||
<FieldError errors={initialState.errors?.date} />
|
||
</div>
|
||
<div className="form-group col-sm-6">
|
||
<label htmlFor="time">
|
||
<span className="oi oi-clock" /> 時刻
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="time"
|
||
name="time"
|
||
className={classNames({
|
||
'form-control': true,
|
||
'is-invalid': initialState.errors?.time || initialState.errors?.datetime,
|
||
})}
|
||
pattern="^([01][0-9]|2[0-3]):[0-5][0-9]$"
|
||
required
|
||
value={time}
|
||
onChange={(e) => setTime(e.target.value)}
|
||
disabled={isRealtime}
|
||
/>
|
||
<FieldError errors={initialState.errors?.time} />
|
||
</div>
|
||
<StandaloneFieldError errors={initialState.errors?.datetime} />
|
||
</div>
|
||
<div className="form-row">
|
||
<div className="form-group col-sm-12">
|
||
<label htmlFor="tagInput">
|
||
<span className="oi oi-tags" /> タグ
|
||
</label>
|
||
<TagInput
|
||
id="tagInput"
|
||
name="tags"
|
||
values={tags}
|
||
isInvalid={!!initialState.errors?.tags}
|
||
onChange={(v) => setTags(v)}
|
||
/>
|
||
<small className="form-text text-muted">Tab, Enter, 半角スペースのいずれかで入力確定します。</small>
|
||
<FieldError errors={initialState.errors?.tags} />
|
||
</div>
|
||
</div>
|
||
<div className="form-row">
|
||
<div className="form-group col-sm-12">
|
||
<label htmlFor="link">
|
||
<span className="oi oi-link-intact" /> オカズリンク
|
||
</label>
|
||
<input
|
||
type="text"
|
||
id="link"
|
||
name="link"
|
||
autoComplete="off"
|
||
className={classNames({ 'form-control': true, 'is-invalid': initialState.errors?.link })}
|
||
placeholder="http://..."
|
||
value={link}
|
||
onChange={(e) => setLink(e.target.value)}
|
||
onBlur={() => setLinkForPreview(link)}
|
||
/>
|
||
<small className="form-text text-muted">オカズのURLを貼り付けて登録することができます。</small>
|
||
<FieldError errors={initialState.errors?.link} />
|
||
</div>
|
||
</div>
|
||
<MetadataPreview link={linkForPreview} tags={tags} onClickTag={(v) => setTags(tags.concat(v))} />
|
||
<div className="form-row">
|
||
<div className="form-group col-sm-12">
|
||
<label htmlFor="note">
|
||
<span className="oi oi-comment-square" /> ノート
|
||
</label>
|
||
<textarea
|
||
id="note"
|
||
name="note"
|
||
className={classNames({ 'form-control': true, 'is-invalid': initialState.errors?.note })}
|
||
rows={4}
|
||
value={note}
|
||
onChange={(e) => setNote(e.target.value)}
|
||
/>
|
||
<small className="form-text text-muted">最大 500 文字</small>
|
||
<FieldError errors={initialState.errors?.note} />
|
||
</div>
|
||
</div>
|
||
<div className="form-row mt-4">
|
||
<p>オプション</p>
|
||
<div className="form-group col-sm-12">
|
||
<CheckBox
|
||
id="isPrivate"
|
||
name="is_private"
|
||
className="mb-3"
|
||
checked={isPrivate}
|
||
onChange={(v) => setPrivate(v)}
|
||
>
|
||
<span className="oi oi-lock-locked" /> このチェックインを非公開にする
|
||
</CheckBox>
|
||
<CheckBox
|
||
id="isTooSensitive"
|
||
name="is_too_sensitive"
|
||
className="mb-3"
|
||
checked={isTooSensitive}
|
||
onChange={(v) => setTooSensitive(v)}
|
||
>
|
||
<span className="oi oi-warning" /> チェックイン対象のオカズをより過激なオカズとして設定する
|
||
</CheckBox>
|
||
<CheckBox
|
||
id="discardElapsedTime"
|
||
name="discard_elapsed_time"
|
||
className="mb-3"
|
||
checked={discardElapsedTime}
|
||
onChange={(v) => setDiscardElapsedTime(v)}
|
||
>
|
||
<span className="oi oi-timer" /> 前回チェックインからの経過時間を記録しない
|
||
<br />
|
||
<small className="form-text text-muted">
|
||
長期間お使いにならなかった場合など、経過時間に意味が無い時のリセット用オプションです。
|
||
<ul className="pl-3">
|
||
<li>最長・最短記録の計算から除外されます。</li>
|
||
<li>平均記録の起点がこのチェックインになります。</li>
|
||
</ul>
|
||
</small>
|
||
</CheckBox>
|
||
</div>
|
||
</div>
|
||
<div className="text-center">
|
||
<button className="btn btn-primary" type="submit">
|
||
チェックイン
|
||
</button>
|
||
</div>
|
||
</>
|
||
);
|
||
};
|