added full settings options

This commit is contained in:
Ray
2026-07-12 14:42:44 -04:00
parent fc8290d668
commit e239dd91b7
23 changed files with 1439 additions and 188 deletions
+21 -9
View File
@@ -23,6 +23,8 @@ import { downloadROPDF, printROPDF } from '../lib/roPdf';
import { useSettings } from '../store/settings';
import { logError } from '../lib/userMessages';
import { parseRepairOrderServices, syncAssignmentsForROServices } from '../lib/technicianData';
import { formatServiceTextFields } from '../lib/dataFormatting';
import { spellCheckProps } from '../lib/spellcheck';
type TabId = 'details' | 'services' | 'financial' | 'history' | 'timeline';
@@ -166,12 +168,14 @@ function PricingBadge({ mode }: { mode: 'full' | 'split' }) {
return <span className="inline-flex items-center rounded-full bg-blue-100 px-2 py-0.5 text-[10px] font-medium text-blue-700 dark:bg-blue-900/30 dark:text-blue-300">Split</span>;
}
function EditableServiceRow({ service, index, onChange, onRemove, defaultLaborRate = 145 }: {
function EditableServiceRow({ service, index, onChange, onRemove, defaultLaborRate = 145, spellCheck, lang }: {
service: Partial<ROService>;
index: number;
onChange: (index: number, field: string, value: string | number | boolean) => void;
onRemove: (index: number) => void;
defaultLaborRate?: number;
spellCheck?: boolean;
lang?: string;
}) {
const pricingMode = service.pricingMode || 'full';
const isSplit = pricingMode === 'split';
@@ -246,7 +250,7 @@ function EditableServiceRow({ service, index, onChange, onRemove, defaultLaborRa
</div>
<div className="sm:col-span-2 lg:col-span-1">
<label className="block text-xs text-gray-500 dark:text-gray-400">Description</label>
<textarea value={service.description || ''} onChange={(e) => onChange(index, 'description', e.target.value)} placeholder="Description..." rows={1} className="mt-0.5 w-full rounded border border-gray-300 bg-white px-2 py-1.5 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100" />
<textarea value={service.description || ''} onChange={(e) => onChange(index, 'description', e.target.value)} placeholder="Description..." rows={1} spellCheck={spellCheck} lang={lang} className="mt-0.5 w-full rounded border border-gray-300 bg-white px-2 py-1.5 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100" />
</div>
{pricingMode === 'full' ? (
<>
@@ -305,9 +309,11 @@ function Input({ value, onChange, placeholder, type = 'text', icon }: { value: s
);
}
function Textarea({ value, onChange, placeholder, rows = 3 }: { value: string; onChange: (value: string) => void; placeholder?: string; rows?: number }) {
function Textarea({ value, onChange, placeholder, rows = 3, spellCheck, lang }: { value: string; onChange: (value: string) => void; placeholder?: string; rows?: number; spellCheck?: boolean; lang?: string }) {
return (
<textarea value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} rows={rows}
spellCheck={spellCheck}
lang={lang}
className="w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-green-500 focus:outline-none focus:ring-2 focus:ring-green-500/20 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100"
/>
);
@@ -321,6 +327,8 @@ export default function RODetailModal() {
const navigate = useNavigate();
const { showToast } = useToast();
const settings = useSettings();
const serviceTextSpellCheck = spellCheckProps(settings, settings.spellCheckServiceText);
const internalNotesSpellCheck = spellCheckProps(settings, settings.spellCheckInternalNotes);
const statusConfig = useMemo(() => getStatusConfig(settings), [settings]);
const modalBackground = (location.state as { backgroundLocation?: unknown })?.backgroundLocation;
@@ -864,7 +872,8 @@ export default function RODetailModal() {
if (!ro) return;
setSaving(true);
try {
const cleaned = localServices.map((s) => ({
const cleaned = localServices.map((s) => {
const service = {
id: s.id,
name: s.name,
description: s.description || '',
@@ -876,7 +885,9 @@ export default function RODetailModal() {
technician: s.technician || '',
pricingMode: s.pricingMode || 'full',
applyShopCharge: s.applyShopCharge !== false,
}));
};
return settings.applyServiceFormattingToROs ? formatServiceTextFields(service, settings) : service;
});
const payload = { services: JSON.stringify(cleaned) };
await pb.collection('repairOrders').update(ro.id, payload);
@@ -916,7 +927,7 @@ export default function RODetailModal() {
const partsPrice = Number(newServiceForm.partsPrice) || 0;
const isSplit = newServiceForm.pricingMode === 'split';
const description = newServiceForm.description.trim();
const data = {
const data = formatServiceTextFields({
name: newServiceForm.name.trim(),
category: newServiceForm.category.trim(),
description,
@@ -927,7 +938,7 @@ export default function RODetailModal() {
price: isSplit ? laborPrice + partsPrice : fullPrice,
active: true,
userId,
};
}, settings.applyServiceFormattingToCatalog ? settings : { ...settings, serviceNameFormat: 'preserve', serviceDescriptionFormat: 'preserve', serviceRecommendationFormat: 'preserve', serviceCategoryFormat: 'preserve', trimServiceText: true, addPeriodToServiceDescriptions: false });
try {
const created = await pb.collection('services').create(data) as Record<string, unknown>;
const newSvc: ServiceItem = {
@@ -1054,6 +1065,7 @@ export default function RODetailModal() {
onChange={(e) => setReminderNotes(e.target.value)}
placeholder="e.g. Next oil change reminder"
rows={2}
{...internalNotesSpellCheck}
className="mt-1 w-full rounded border border-blue-300 bg-white px-2 py-1.5 text-sm dark:border-blue-700 dark:bg-gray-800 dark:text-gray-100"
/>
</div>
@@ -1315,7 +1327,7 @@ export default function RODetailModal() {
<div className="mt-3">
<FormField label="Description">
<div className="space-y-2">
<Textarea value={newServiceForm.description} onChange={(v) => setNewServiceForm((p) => ({ ...p, description: v }))} placeholder="Brief description..." rows={2} />
<Textarea value={newServiceForm.description} onChange={(v) => setNewServiceForm((p) => ({ ...p, description: v }))} placeholder="Brief description..." rows={2} {...serviceTextSpellCheck} />
</div>
</FormField>
</div>
@@ -1373,7 +1385,7 @@ export default function RODetailModal() {
</div>
<div className="space-y-3">
{localServices.map((svc, i) => (
<EditableServiceRow key={i} service={svc} index={i} onChange={handleServiceChange} onRemove={handleRemoveService} defaultLaborRate={settings.defaultLaborRate || 145} />
<EditableServiceRow key={i} service={svc} index={i} onChange={handleServiceChange} onRemove={handleRemoveService} defaultLaborRate={settings.defaultLaborRate || 145} {...serviceTextSpellCheck} />
))}
{localServices.length === 0 && (
<p className="text-center text-sm text-gray-400">No services on this RO yet.</p>