|
|
|
@ -1,11 +1,12 @@
|
|
|
|
import { useState } from 'react'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
|
|
import { fromRupees } from '@sims/domain'
|
|
|
|
import { fromRupees } from '@sims/domain'
|
|
|
|
import { Button, Field, Notice, Toolbar } from '@sims/ui'
|
|
|
|
import { Button, Dialog, Field, FormField, FormGrid, Notice, Toolbar, useToast } from '@sims/ui'
|
|
|
|
import {
|
|
|
|
import {
|
|
|
|
createAmc, createInteraction, createRecurringPlan, patchClientModule, recordPayment,
|
|
|
|
createAmc, createInteraction, createRecurringPlan, patchClientModule, recordPayment,
|
|
|
|
|
|
|
|
updateAmc, updateInteraction, updateRecurringPlan,
|
|
|
|
CLIENT_MODULE_STATUSES, KIND_LABEL, PAYMENT_MODES,
|
|
|
|
CLIENT_MODULE_STATUSES, KIND_LABEL, PAYMENT_MODES,
|
|
|
|
type ClientModule, type ClientModuleStatus, type Employee, type InteractionType,
|
|
|
|
type AmcContract, type ClientModule, type ClientModuleStatus, type Employee, type Interaction,
|
|
|
|
type Kind, type Module, type PaymentMode,
|
|
|
|
type InteractionType, type Kind, type Module, type PaymentMode, type RecurringPlan,
|
|
|
|
} from '../api'
|
|
|
|
} from '../api'
|
|
|
|
|
|
|
|
|
|
|
|
const today = () => new Date().toISOString().slice(0, 10)
|
|
|
|
const today = () => new Date().toISOString().slice(0, 10)
|
|
|
|
@ -35,17 +36,33 @@ export function AccountOwnerSelect(props: {
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Owner-only: create a recurring plan on one of the client's modules. */
|
|
|
|
/** Owner-only: create or edit a recurring plan on one of the client's modules. */
|
|
|
|
export function NewRecurringForm(props: {
|
|
|
|
export function PlanDialog(props: {
|
|
|
|
clientId: string; options: { id: string; label: string }[]; onDone: () => void
|
|
|
|
open: boolean; onClose: () => void; clientId: string
|
|
|
|
|
|
|
|
options: { id: string; label: string }[]; initial?: RecurringPlan; onDone: () => void
|
|
|
|
}) {
|
|
|
|
}) {
|
|
|
|
|
|
|
|
const toast = useToast()
|
|
|
|
|
|
|
|
const editing = props.initial !== undefined
|
|
|
|
const [f, setF] = useState({ clientModuleId: '', cadence: 'monthly', amountRs: '', nextRun: today(), policy: 'manual' })
|
|
|
|
const [f, setF] = useState({ clientModuleId: '', cadence: 'monthly', amountRs: '', nextRun: today(), policy: 'manual' })
|
|
|
|
const [error, setError] = useState<string | undefined>()
|
|
|
|
const [error, setError] = useState<string | undefined>()
|
|
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
if (!props.open) return
|
|
|
|
|
|
|
|
const p = props.initial
|
|
|
|
|
|
|
|
setError(undefined)
|
|
|
|
|
|
|
|
setF({
|
|
|
|
|
|
|
|
clientModuleId: p?.clientModuleId ?? '', cadence: p?.cadence ?? 'monthly',
|
|
|
|
|
|
|
|
amountRs: p?.amountPaise !== null && p?.amountPaise !== undefined ? String(p.amountPaise / 100) : '',
|
|
|
|
|
|
|
|
nextRun: p?.nextRun ?? today(), policy: p?.policy ?? 'manual',
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}, [props.open, props.initial])
|
|
|
|
|
|
|
|
|
|
|
|
const set = (k: keyof typeof f) => (e: { target: { value: string } }) =>
|
|
|
|
const set = (k: keyof typeof f) => (e: { target: { value: string } }) =>
|
|
|
|
setF((p) => ({ ...p, [k]: e.target.value }))
|
|
|
|
setF((p) => ({ ...p, [k]: e.target.value }))
|
|
|
|
|
|
|
|
|
|
|
|
const save = () => {
|
|
|
|
const save = () => {
|
|
|
|
|
|
|
|
if (saving) return
|
|
|
|
if (f.clientModuleId === '') { setError('Pick a subscribed module'); return }
|
|
|
|
if (f.clientModuleId === '') { setError('Pick a subscribed module'); return }
|
|
|
|
const amount = f.amountRs === '' ? undefined : Number(f.amountRs)
|
|
|
|
const amount = f.amountRs === '' ? undefined : Number(f.amountRs)
|
|
|
|
if (amount !== undefined && (!Number.isFinite(amount) || amount <= 0)) {
|
|
|
|
if (amount !== undefined && (!Number.isFinite(amount) || amount <= 0)) {
|
|
|
|
@ -53,54 +70,82 @@ export function NewRecurringForm(props: {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setError(undefined)
|
|
|
|
setError(undefined)
|
|
|
|
setSaving(true)
|
|
|
|
setSaving(true)
|
|
|
|
createRecurringPlan(props.clientId, {
|
|
|
|
const body = {
|
|
|
|
clientModuleId: f.clientModuleId, cadence: f.cadence, nextRun: f.nextRun, policy: f.policy,
|
|
|
|
clientModuleId: f.clientModuleId, cadence: f.cadence, nextRun: f.nextRun, policy: f.policy,
|
|
|
|
...(amount !== undefined ? { amountPaise: fromRupees(amount) } : {}),
|
|
|
|
...(amount !== undefined ? { amountPaise: fromRupees(amount) } : {}),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
.then(() => { setF({ clientModuleId: '', cadence: 'monthly', amountRs: '', nextRun: today(), policy: 'manual' }); props.onDone() })
|
|
|
|
const call = editing ? updateRecurringPlan(props.initial!.id, body) : createRecurringPlan(props.clientId, body)
|
|
|
|
|
|
|
|
call
|
|
|
|
|
|
|
|
.then(() => { toast.ok('Plan saved'); props.onDone(); props.onClose() })
|
|
|
|
.catch((e: Error) => setError(e.message))
|
|
|
|
.catch((e: Error) => setError(e.message))
|
|
|
|
.finally(() => setSaving(false))
|
|
|
|
.finally(() => setSaving(false))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<div className="wf-card">
|
|
|
|
<Dialog
|
|
|
|
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'flex-end' }}>
|
|
|
|
open={props.open}
|
|
|
|
<Field label="Module">
|
|
|
|
onClose={() => { if (!saving) props.onClose() }}
|
|
|
|
<select className="wf" value={f.clientModuleId} onChange={set('clientModuleId')}>
|
|
|
|
title={editing ? 'Edit recurring plan' : 'New recurring plan'}
|
|
|
|
|
|
|
|
footer={
|
|
|
|
|
|
|
|
<>
|
|
|
|
|
|
|
|
<Button pill onClick={() => { if (!saving) props.onClose() }}>Cancel</Button>
|
|
|
|
|
|
|
|
<Button pill tone="primary" onClick={save}>{saving ? 'Saving…' : editing ? 'Save changes' : 'New recurring plan'}</Button>
|
|
|
|
|
|
|
|
</>
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<FormGrid>
|
|
|
|
|
|
|
|
<FormField label="Module">
|
|
|
|
|
|
|
|
<select className="wf" autoFocus value={f.clientModuleId} onChange={set('clientModuleId')}>
|
|
|
|
<option value="">Module…</option>
|
|
|
|
<option value="">Module…</option>
|
|
|
|
{props.options.map((o) => <option key={o.id} value={o.id}>{o.label}</option>)}
|
|
|
|
{props.options.map((o) => <option key={o.id} value={o.id}>{o.label}</option>)}
|
|
|
|
</select>
|
|
|
|
</select>
|
|
|
|
</Field>
|
|
|
|
</FormField>
|
|
|
|
<Field label="Cadence">
|
|
|
|
<FormField label="Cadence">
|
|
|
|
<select className="wf" value={f.cadence} onChange={set('cadence')}>
|
|
|
|
<select className="wf" value={f.cadence} onChange={set('cadence')}>
|
|
|
|
<option value="monthly">monthly</option>
|
|
|
|
<option value="monthly">monthly</option>
|
|
|
|
<option value="yearly">yearly</option>
|
|
|
|
<option value="yearly">yearly</option>
|
|
|
|
</select>
|
|
|
|
</select>
|
|
|
|
</Field>
|
|
|
|
</FormField>
|
|
|
|
<Field label="Amount (₹, optional)"><input className="wf num" style={{ width: 120 }} value={f.amountRs} onChange={set('amountRs')} /></Field>
|
|
|
|
<FormField label="Amount (₹, optional)"><input className="wf num" value={f.amountRs} onChange={set('amountRs')} /></FormField>
|
|
|
|
<Field label="Next run"><input type="date" className="wf" value={f.nextRun} onChange={set('nextRun')} /></Field>
|
|
|
|
<FormField label="Next run"><input type="date" className="wf" value={f.nextRun} onChange={set('nextRun')} /></FormField>
|
|
|
|
<Field label="Policy">
|
|
|
|
<FormField label="Policy">
|
|
|
|
<select className="wf" value={f.policy} onChange={set('policy')}>
|
|
|
|
<select className="wf" value={f.policy} onChange={set('policy')}>
|
|
|
|
<option value="manual">manual</option>
|
|
|
|
<option value="manual">manual</option>
|
|
|
|
<option value="auto">auto</option>
|
|
|
|
<option value="auto">auto</option>
|
|
|
|
</select>
|
|
|
|
</select>
|
|
|
|
</Field>
|
|
|
|
</FormField>
|
|
|
|
<Button tone="primary" onClick={save}>{saving ? 'Saving…' : 'New recurring plan'}</Button>
|
|
|
|
</FormGrid>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{error !== undefined && <Notice tone="err">{error}</Notice>}
|
|
|
|
{error !== undefined && <Notice tone="err">{error}</Notice>}
|
|
|
|
</div>
|
|
|
|
</Dialog>
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Owner-only: create an AMC contract — amount entered in rupees, stored in paise. */
|
|
|
|
/** Owner-only: create or edit an AMC contract — amount entered in rupees, stored in paise. */
|
|
|
|
export function NewAmcForm(props: { clientId: string; onDone: () => void }) {
|
|
|
|
export function AmcDialog(props: {
|
|
|
|
|
|
|
|
open: boolean; onClose: () => void; clientId: string; initial?: AmcContract; onDone: () => void
|
|
|
|
|
|
|
|
}) {
|
|
|
|
|
|
|
|
const toast = useToast()
|
|
|
|
|
|
|
|
const editing = props.initial !== undefined
|
|
|
|
const [f, setF] = useState({ coverage: '', periodFrom: today(), periodTo: '', amountRs: '', reminderDays: '30' })
|
|
|
|
const [f, setF] = useState({ coverage: '', periodFrom: today(), periodTo: '', amountRs: '', reminderDays: '30' })
|
|
|
|
const [error, setError] = useState<string | undefined>()
|
|
|
|
const [error, setError] = useState<string | undefined>()
|
|
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
if (!props.open) return
|
|
|
|
|
|
|
|
const a = props.initial
|
|
|
|
|
|
|
|
setError(undefined)
|
|
|
|
|
|
|
|
setF({
|
|
|
|
|
|
|
|
coverage: a?.coverage ?? '', periodFrom: a?.periodFrom ?? today(), periodTo: a?.periodTo ?? '',
|
|
|
|
|
|
|
|
amountRs: a !== undefined ? String(a.amountPaise / 100) : '',
|
|
|
|
|
|
|
|
reminderDays: a !== undefined ? String(a.renewalReminderDays) : '30',
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}, [props.open, props.initial])
|
|
|
|
|
|
|
|
|
|
|
|
const set = (k: keyof typeof f) => (e: { target: { value: string } }) =>
|
|
|
|
const set = (k: keyof typeof f) => (e: { target: { value: string } }) =>
|
|
|
|
setF((p) => ({ ...p, [k]: e.target.value }))
|
|
|
|
setF((p) => ({ ...p, [k]: e.target.value }))
|
|
|
|
|
|
|
|
|
|
|
|
const save = () => {
|
|
|
|
const save = () => {
|
|
|
|
|
|
|
|
if (saving) return
|
|
|
|
const amount = Number(f.amountRs)
|
|
|
|
const amount = Number(f.amountRs)
|
|
|
|
if (!Number.isFinite(amount) || amount <= 0) { setError('Enter the contract amount in rupees'); return }
|
|
|
|
if (!Number.isFinite(amount) || amount <= 0) { setError('Enter the contract amount in rupees'); return }
|
|
|
|
if (f.periodTo === '') { setError('Pick the period end date'); return }
|
|
|
|
if (f.periodTo === '') { setError('Pick the period end date'); return }
|
|
|
|
@ -108,78 +153,115 @@ export function NewAmcForm(props: { clientId: string; onDone: () => void }) {
|
|
|
|
if (!Number.isInteger(days) || days < 0) { setError('Reminder days must be a non-negative integer'); return }
|
|
|
|
if (!Number.isInteger(days) || days < 0) { setError('Reminder days must be a non-negative integer'); return }
|
|
|
|
setError(undefined)
|
|
|
|
setError(undefined)
|
|
|
|
setSaving(true)
|
|
|
|
setSaving(true)
|
|
|
|
createAmc(props.clientId, {
|
|
|
|
const body = {
|
|
|
|
coverage: f.coverage, periodFrom: f.periodFrom, periodTo: f.periodTo,
|
|
|
|
coverage: f.coverage, periodFrom: f.periodFrom, periodTo: f.periodTo,
|
|
|
|
amountPaise: fromRupees(amount), renewalReminderDays: days,
|
|
|
|
amountPaise: fromRupees(amount), renewalReminderDays: days,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
.then(() => { setF({ coverage: '', periodFrom: today(), periodTo: '', amountRs: '', reminderDays: '30' }); props.onDone() })
|
|
|
|
const call = editing ? updateAmc(props.initial!.id, body) : createAmc(props.clientId, body)
|
|
|
|
|
|
|
|
call
|
|
|
|
|
|
|
|
.then(() => { toast.ok('AMC saved'); props.onDone(); props.onClose() })
|
|
|
|
.catch((e: Error) => setError(e.message))
|
|
|
|
.catch((e: Error) => setError(e.message))
|
|
|
|
.finally(() => setSaving(false))
|
|
|
|
.finally(() => setSaving(false))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<div className="wf-card">
|
|
|
|
<Dialog
|
|
|
|
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'flex-end' }}>
|
|
|
|
open={props.open}
|
|
|
|
<Field label="Coverage"><input className="wf" value={f.coverage} onChange={set('coverage')} /></Field>
|
|
|
|
onClose={() => { if (!saving) props.onClose() }}
|
|
|
|
<Field label="Period from"><input type="date" className="wf" value={f.periodFrom} onChange={set('periodFrom')} /></Field>
|
|
|
|
title={editing ? 'Edit AMC contract' : 'New AMC contract'}
|
|
|
|
<Field label="Period to"><input type="date" className="wf" value={f.periodTo} onChange={set('periodTo')} /></Field>
|
|
|
|
footer={
|
|
|
|
<Field label="Amount (₹)"><input className="wf num" style={{ width: 120 }} value={f.amountRs} onChange={set('amountRs')} /></Field>
|
|
|
|
<>
|
|
|
|
<Field label="Reminder days"><input className="wf num" style={{ width: 80 }} value={f.reminderDays} onChange={set('reminderDays')} /></Field>
|
|
|
|
<Button pill onClick={() => { if (!saving) props.onClose() }}>Cancel</Button>
|
|
|
|
<Button tone="primary" onClick={save}>{saving ? 'Saving…' : 'New AMC'}</Button>
|
|
|
|
<Button pill tone="primary" onClick={save}>{saving ? 'Saving…' : editing ? 'Save changes' : 'New AMC'}</Button>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<FormGrid>
|
|
|
|
|
|
|
|
<FormField label="Coverage" wide><input className="wf" autoFocus value={f.coverage} onChange={set('coverage')} /></FormField>
|
|
|
|
|
|
|
|
<FormField label="Period from"><input type="date" className="wf" value={f.periodFrom} onChange={set('periodFrom')} /></FormField>
|
|
|
|
|
|
|
|
<FormField label="Period to"><input type="date" className="wf" value={f.periodTo} onChange={set('periodTo')} /></FormField>
|
|
|
|
|
|
|
|
<FormField label="Amount (₹)"><input className="wf num" value={f.amountRs} onChange={set('amountRs')} /></FormField>
|
|
|
|
|
|
|
|
<FormField label="Reminder days"><input className="wf num" value={f.reminderDays} onChange={set('reminderDays')} /></FormField>
|
|
|
|
|
|
|
|
</FormGrid>
|
|
|
|
{error !== undefined && <Notice tone="err">{error}</Notice>}
|
|
|
|
{error !== undefined && <Notice tone="err">{error}</Notice>}
|
|
|
|
</div>
|
|
|
|
</Dialog>
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** All roles: log a call / visit / training …; a follow-up date feeds the daily scan. */
|
|
|
|
/** All roles: log or edit a call / visit / training …; a follow-up date feeds the daily scan. */
|
|
|
|
export function LogInteractionForm(props: { clientId: string; types: InteractionType[]; onDone: () => void }) {
|
|
|
|
export function InteractionDialog(props: {
|
|
|
|
|
|
|
|
open: boolean; onClose: () => void; clientId: string
|
|
|
|
|
|
|
|
types: InteractionType[]; initial?: Interaction; onDone: () => void
|
|
|
|
|
|
|
|
}) {
|
|
|
|
|
|
|
|
const toast = useToast()
|
|
|
|
|
|
|
|
const editing = props.initial !== undefined
|
|
|
|
const [f, setF] = useState({ typeCode: '', onDate: today(), notes: '', outcome: '', followUpOn: '' })
|
|
|
|
const [f, setF] = useState({ typeCode: '', onDate: today(), notes: '', outcome: '', followUpOn: '' })
|
|
|
|
const [error, setError] = useState<string | undefined>()
|
|
|
|
const [error, setError] = useState<string | undefined>()
|
|
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
const [saving, setSaving] = useState(false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
if (!props.open) return
|
|
|
|
|
|
|
|
const i = props.initial
|
|
|
|
|
|
|
|
setError(undefined)
|
|
|
|
|
|
|
|
setF({
|
|
|
|
|
|
|
|
typeCode: i?.typeCode ?? '', onDate: i?.onDate ?? today(), notes: i?.notes ?? '',
|
|
|
|
|
|
|
|
outcome: i?.outcome ?? '', followUpOn: i?.followUpOn ?? '',
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}, [props.open, props.initial])
|
|
|
|
|
|
|
|
|
|
|
|
const set = (k: keyof typeof f) => (e: { target: { value: string } }) =>
|
|
|
|
const set = (k: keyof typeof f) => (e: { target: { value: string } }) =>
|
|
|
|
setF((p) => ({ ...p, [k]: e.target.value }))
|
|
|
|
setF((p) => ({ ...p, [k]: e.target.value }))
|
|
|
|
|
|
|
|
|
|
|
|
const save = () => {
|
|
|
|
const save = () => {
|
|
|
|
|
|
|
|
if (saving) return
|
|
|
|
if (f.typeCode === '') { setError('Pick an interaction type'); return }
|
|
|
|
if (f.typeCode === '') { setError('Pick an interaction type'); return }
|
|
|
|
setError(undefined)
|
|
|
|
setError(undefined)
|
|
|
|
setSaving(true)
|
|
|
|
setSaving(true)
|
|
|
|
createInteraction(props.clientId, {
|
|
|
|
const body = {
|
|
|
|
typeCode: f.typeCode, onDate: f.onDate, notes: f.notes,
|
|
|
|
typeCode: f.typeCode, onDate: f.onDate, notes: f.notes,
|
|
|
|
...(f.outcome !== '' ? { outcome: f.outcome } : {}),
|
|
|
|
...(f.outcome !== '' ? { outcome: f.outcome } : {}),
|
|
|
|
...(f.followUpOn !== '' ? { followUpOn: f.followUpOn } : {}),
|
|
|
|
...(f.followUpOn !== '' ? { followUpOn: f.followUpOn } : {}),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
.then(() => { setF({ typeCode: '', onDate: today(), notes: '', outcome: '', followUpOn: '' }); props.onDone() })
|
|
|
|
const call = editing ? updateInteraction(props.initial!.id, body) : createInteraction(props.clientId, body)
|
|
|
|
|
|
|
|
call
|
|
|
|
|
|
|
|
.then(() => { toast.ok('Interaction saved'); props.onDone(); props.onClose() })
|
|
|
|
.catch((e: Error) => setError(e.message))
|
|
|
|
.catch((e: Error) => setError(e.message))
|
|
|
|
.finally(() => setSaving(false))
|
|
|
|
.finally(() => setSaving(false))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<div className="wf-card">
|
|
|
|
<Dialog
|
|
|
|
<div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'flex-end' }}>
|
|
|
|
open={props.open}
|
|
|
|
<Field label="Type">
|
|
|
|
onClose={() => { if (!saving) props.onClose() }}
|
|
|
|
<select className="wf" value={f.typeCode} onChange={set('typeCode')}>
|
|
|
|
title={editing ? 'Edit interaction' : 'Log interaction'}
|
|
|
|
|
|
|
|
footer={
|
|
|
|
|
|
|
|
<>
|
|
|
|
|
|
|
|
<Button pill onClick={() => { if (!saving) props.onClose() }}>Cancel</Button>
|
|
|
|
|
|
|
|
<Button pill tone="primary" onClick={save}>{saving ? (editing ? 'Saving…' : 'Logging…') : editing ? 'Save changes' : 'Log interaction'}</Button>
|
|
|
|
|
|
|
|
</>
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<FormGrid>
|
|
|
|
|
|
|
|
<FormField label="Type">
|
|
|
|
|
|
|
|
<select className="wf" autoFocus value={f.typeCode} onChange={set('typeCode')}>
|
|
|
|
<option value="">Type…</option>
|
|
|
|
<option value="">Type…</option>
|
|
|
|
{props.types.map((t) => <option key={t.code} value={t.code}>{t.label}</option>)}
|
|
|
|
{props.types.map((t) => <option key={t.code} value={t.code}>{t.label}</option>)}
|
|
|
|
</select>
|
|
|
|
</select>
|
|
|
|
</Field>
|
|
|
|
</FormField>
|
|
|
|
<Field label="On"><input type="date" className="wf" value={f.onDate} onChange={set('onDate')} /></Field>
|
|
|
|
<FormField label="On"><input type="date" className="wf" value={f.onDate} onChange={set('onDate')} /></FormField>
|
|
|
|
<Field label="Outcome">
|
|
|
|
<FormField label="Outcome">
|
|
|
|
<select className="wf" value={f.outcome} onChange={set('outcome')}>
|
|
|
|
<select className="wf" value={f.outcome} onChange={set('outcome')}>
|
|
|
|
<option value="">—</option>
|
|
|
|
<option value="">—</option>
|
|
|
|
<option value="positive">positive</option>
|
|
|
|
<option value="positive">positive</option>
|
|
|
|
<option value="neutral">neutral</option>
|
|
|
|
<option value="neutral">neutral</option>
|
|
|
|
<option value="negative">negative</option>
|
|
|
|
<option value="negative">negative</option>
|
|
|
|
</select>
|
|
|
|
</select>
|
|
|
|
</Field>
|
|
|
|
</FormField>
|
|
|
|
<Field label="Follow-up on"><input type="date" className="wf" value={f.followUpOn} onChange={set('followUpOn')} /></Field>
|
|
|
|
<FormField label="Follow-up on"><input type="date" className="wf" value={f.followUpOn} onChange={set('followUpOn')} /></FormField>
|
|
|
|
<Button tone="primary" onClick={save}>{saving ? 'Logging…' : 'Log interaction'}</Button>
|
|
|
|
<FormField label="Notes" wide><textarea className="wf" rows={2} value={f.notes} onChange={set('notes')} /></FormField>
|
|
|
|
</div>
|
|
|
|
</FormGrid>
|
|
|
|
<div style={{ marginTop: 8 }}>
|
|
|
|
|
|
|
|
<Field label="Notes"><textarea className="wf" rows={2} style={{ width: '100%' }} value={f.notes} onChange={set('notes')} /></Field>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{error !== undefined && <Notice tone="err">{error}</Notice>}
|
|
|
|
{error !== undefined && <Notice tone="err">{error}</Notice>}
|
|
|
|
</div>
|
|
|
|
</Dialog>
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|