@ -2,12 +2,13 @@ import { useEffect, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import { formatINR } from '@sims/domain'
import {
Button , DataTable , Dialog , EmptyState , ErrorState , FormField , FormGrid ,
Button , DataTable , Dialog , EmptyState , ErrorState , FormField , FormGrid , Toolbar ,
Notice , PageHeader , Skeleton , ThemeSwitcher , useToast ,
} from '@sims/ui'
import {
createSchedule , getAwsRanking , getCompanyProfile , getEmailStatus , getReminderSettings ,
getSharingSettings , putCompanyProfile , putReminderSettings , putSharingSettings , role ,
getOperationsSettings , putOperationsSettings , type OperationsSettings ,
} from '../api'
import { COMPANY_FIELDS , TemplatePanel } from './DocumentTemplate'
import { useData } from './Clients'
@ -15,12 +16,13 @@ import { useData } from './Clients'
const inr = ( p : number ) = > formatINR ( p , { symbol : false } )
type Dict = Record < string , string >
type SectionKey = 'company' | 'template' | 'reminders' | ' sharing' | 'integrations' | 'appearance'
type SectionKey = 'company' | 'template' | 'reminders' | ' operations' | ' sharing' | 'integrations' | 'appearance'
const SECTIONS : { key : SectionKey ; label : string ; ownerOnly : boolean } [ ] = [
{ key : 'company' , label : 'Company' , ownerOnly : true } ,
{ key : 'template' , label : 'Document template' , ownerOnly : true } ,
{ key : 'reminders' , label : 'Reminders' , ownerOnly : true } ,
{ key : 'operations' , label : 'Operations' , ownerOnly : true } ,
{ key : 'sharing' , label : 'Sharing' , ownerOnly : true } ,
{ key : 'integrations' , label : 'Integrations' , ownerOnly : true } ,
{ key : 'appearance' , label : 'Appearance' , ownerOnly : false } ,
@ -64,6 +66,7 @@ export function Settings() {
{ active === 'company' && < CompanyPanel / > }
{ active === 'template' && < TemplatePanel / > }
{ active === 'reminders' && < RemindersPanel / > }
{ active === 'operations' && < OperationsPanel / > }
{ active === 'sharing' && < SharingPanel / > }
{ active === 'integrations' && < IntegrationsPanel / > }
{ active === 'appearance' && < AppearancePanel / > }
@ -73,6 +76,59 @@ export function Settings() {
)
}
/ * * O p e r a t i o n s ( D 2 8 ) — t h e r u n n i n g - t h e - s e r v i c e k n o b s : S M S g a t e w a y + t h r e s h o l d s + t i c k e t S L A .
* All are DB settings resolved by the app ( config over code ) ; editing here is the whole point . * /
function OperationsPanel() {
const toast = useToast ( )
const [ s , setS ] = useState < OperationsSettings | undefined > ( )
const [ saving , setSaving ] = useState ( false )
useEffect ( ( ) = > {
getOperationsSettings ( ) . then ( setS ) . catch ( ( e : Error ) = > toast . err ( e . message ) )
// eslint-disable-next-line react-hooks/exhaustive-deps
} , [ ] )
const save = ( ) = > {
if ( saving || s === undefined ) return
setSaving ( true )
putOperationsSettings ( s )
. then ( ( next ) = > { setS ( next ) ; toast . ok ( 'Operations settings saved.' ) } )
. catch ( ( e : Error ) = > toast . err ( e . message ) )
. finally ( ( ) = > setSaving ( false ) )
}
return (
< div >
< h2 > Operations < / h2 >
< p className = "wf-page-desc" > The knobs behind the SMS balance screen and the ticket desk — change them here , no release needed . < / p >
{ s === undefined ? < Skeleton rows = { 4 } / > : (
< >
< FormGrid >
< FormField label = "SMS balance API URL" wide >
< input className = "wf" value = { s . smsBalanceApiUrl }
onChange = { ( e ) = > setS ( { . . . s , smsBalanceApiUrl : e.target.value } ) } / >
< span style = { { display : 'block' , fontSize : 12 , opacity : 0.6 , marginTop : 4 } } > The provider panel HQ polls for each SMS account ’ s balance . Public host only . < / span >
< / FormField >
< FormField label = "SMS low-balance threshold" >
< input className = "wf num" type = "number" min = { 0 } value = { s . smsLowThreshold }
onChange = { ( e ) = > setS ( { . . . s , smsLowThreshold : Number ( e . target . value ) } ) } / >
< span style = { { display : 'block' , fontSize : 12 , opacity : 0.6 , marginTop : 4 } } > Below this many SMS a client is flagged “ low ” and shows in the upsell list . < / span >
< / FormField >
< FormField label = "Ticket SLA (days)" >
< input className = "wf num" type = "number" min = { 1 } value = { s . ticketSlaDays }
onChange = { ( e ) = > setS ( { . . . s , ticketSlaDays : Number ( e . target . value ) } ) } / >
< span style = { { display : 'block' , fontSize : 12 , opacity : 0.6 , marginTop : 4 } } > A still - open ticket older than this is marked overdue on the desk . < / span >
< / FormField >
< / FormGrid >
< Toolbar >
< Button pill tone = "primary" disabled = { saving } onClick = { save } > { saving ? 'Saving…' : 'Save' } < / Button >
< / Toolbar >
< / >
) }
< / div >
)
}
/ * * C o m p a n y i d e n t i t y — s a m e f i e l d l i s t a s t h e l e t t e r h e a d t e m p l a t e ' s C o m p a n y s e c t i o n
* ( ` COMPANY_FIELDS ` , imported so the two never drift apart ) . * /
function CompanyPanel() {