Offer: valid-till is a date-time picker, never in the past
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2e3c42df5e
commit
470ddc98c4
@ -14,6 +14,12 @@ function fmtDateTime(v: string): string {
|
|||||||
if (!v || isNaN(d.getTime())) return '';
|
if (!v || isNaN(d.getTime())) return '';
|
||||||
return d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }) + ', ' + d.toTimeString().slice(0, 5);
|
return d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }) + ', ' + d.toTimeString().slice(0, 5);
|
||||||
}
|
}
|
||||||
|
/** Now, in the datetime-local input's format, so a validity cannot be set in the past. */
|
||||||
|
function nowLocal(): string {
|
||||||
|
const d = new Date();
|
||||||
|
d.setMinutes(d.getMinutes() - d.getTimezoneOffset());
|
||||||
|
return d.toISOString().slice(0, 16);
|
||||||
|
}
|
||||||
/** A delivery window as the text the offer and the customer read: "12 Nov 2026, 06:00 - 12:00 IST". */
|
/** A delivery window as the text the offer and the customer read: "12 Nov 2026, 06:00 - 12:00 IST". */
|
||||||
function fmtWindow(from: string, to: string): string {
|
function fmtWindow(from: string, to: string): string {
|
||||||
const a = fmtDateTime(from);
|
const a = fmtDateTime(from);
|
||||||
@ -248,18 +254,18 @@ function OpsForm({ e, onDone }: { e: Enquiry; onDone: () => void }) {
|
|||||||
function OfferForm({ e, onDone, revise, onCancel }: { e: Enquiry; onDone: () => void; revise: boolean; onCancel?: () => void }) {
|
function OfferForm({ e, onDone, revise, onCancel }: { e: Enquiry; onDone: () => void; revise: boolean; onCancel?: () => void }) {
|
||||||
const { busy, error, ok, submit } = useSubmit(e, onDone);
|
const { busy, error, ok, submit } = useSubmit(e, onDone);
|
||||||
const [f, setF] = useState({ price: revise && e.price_usd_mt ? String(e.price_usd_mt) : '', basis: e.price_basis ?? `delivered ex-${e.supply_mode === 'pipeline' ? 'wharf' : 'barge'} ${e.port ?? ''}`.trim(),
|
const [f, setF] = useState({ price: revise && e.price_usd_mt ? String(e.price_usd_mt) : '', basis: e.price_basis ?? `delivered ex-${e.supply_mode === 'pipeline' ? 'wharf' : 'barge'} ${e.port ?? ''}`.trim(),
|
||||||
valid: e.offer_valid_till ?? '', terms: e.payment_terms ?? '30 days from date of delivery', remarks: '' });
|
valid: '', terms: e.payment_terms ?? '30 days from date of delivery', remarks: '' });
|
||||||
const [regret, setRegret] = useState(false);
|
const [regret, setRegret] = useState(false);
|
||||||
if (regret) return <ReasonForm e={e} onDone={onDone} activity={ACT.REGRET} field="lost_reason__regret" title="Regret the enquiry" cta="Send regret to customer" onCancel={() => setRegret(false)} />;
|
if (regret) return <ReasonForm e={e} onDone={onDone} activity={ACT.REGRET} field="lost_reason__regret" title="Regret the enquiry" cta="Send regret to customer" onCancel={() => setRegret(false)} />;
|
||||||
const sfx = revise ? 'revise' : 'offer';
|
const sfx = revise ? 'revise' : 'offer';
|
||||||
return (
|
return (
|
||||||
<Card title={revise ? 'Revise the offer' : 'Price and send the offer'} action={<Badge tone="notice">Your action</Badge>}>
|
<Card title={revise ? 'Revise the offer' : 'Price and send the offer'} action={<Badge tone="notice">Your action</Badge>}>
|
||||||
<form className="grid gap-3 sm:grid-cols-2" onSubmit={(ev) => { ev.preventDefault();
|
<form className="grid gap-3 sm:grid-cols-2" onSubmit={(ev) => { ev.preventDefault();
|
||||||
const data: Record<string, unknown> = { [`price_usd_mt__${sfx}`]: Number(f.price), [`offer_valid_till__${sfx}`]: f.valid, [`offer_remarks__${sfx}`]: f.remarks };
|
const data: Record<string, unknown> = { [`price_usd_mt__${sfx}`]: Number(f.price), [`offer_valid_till__${sfx}`]: fmtDateTime(f.valid) + ' IST', [`offer_remarks__${sfx}`]: f.remarks };
|
||||||
if (!revise) { data.price_basis__offer = f.basis; data.payment_terms__offer = f.terms; }
|
if (!revise) { data.price_basis__offer = f.basis; data.payment_terms__offer = f.terms; }
|
||||||
submit(revise ? ACT.REVISE : ACT.OFFER, data); }}>
|
submit(revise ? ACT.REVISE : ACT.OFFER, data); }}>
|
||||||
<Field label="Price (USD per MT)" required><input className="field-input" type="number" step="0.01" min="0" required value={f.price} onChange={(x) => setF({ ...f, price: x.target.value })} /></Field>
|
<Field label="Price (USD per MT)" required><input className="field-input" type="number" step="0.01" min="0" required value={f.price} onChange={(x) => setF({ ...f, price: x.target.value })} /></Field>
|
||||||
<Field label="Offer valid till" required><input className="field-input" required placeholder="26 Oct, 18:00 IST" value={f.valid} onChange={(x) => setF({ ...f, valid: x.target.value })} /></Field>
|
<Field label="Offer valid till (IST)" required><input className="field-input" type="datetime-local" required min={nowLocal()} value={f.valid} onChange={(x) => setF({ ...f, valid: x.target.value })} /></Field>
|
||||||
{!revise && <Field label="Price basis"><input className="field-input" value={f.basis} onChange={(x) => setF({ ...f, basis: x.target.value })} /></Field>}
|
{!revise && <Field label="Price basis"><input className="field-input" value={f.basis} onChange={(x) => setF({ ...f, basis: x.target.value })} /></Field>}
|
||||||
{!revise && <Field label="Payment terms"><input className="field-input" value={f.terms} onChange={(x) => setF({ ...f, terms: x.target.value })} /></Field>}
|
{!revise && <Field label="Payment terms"><input className="field-input" value={f.terms} onChange={(x) => setF({ ...f, terms: x.target.value })} /></Field>}
|
||||||
<div className="sm:col-span-2"><Field label="Remarks for the customer"><textarea className="field-input" rows={2} value={f.remarks} onChange={(x) => setF({ ...f, remarks: x.target.value })} /></Field></div>
|
<div className="sm:col-span-2"><Field label="Remarks for the customer"><textarea className="field-input" rows={2} value={f.remarks} onChange={(x) => setF({ ...f, remarks: x.target.value })} /></Field></div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user