Skip to content

Commit 470e654

Browse files
authored
feat: add optional subtitle field for print header (#20)
Add a new optional subtitle field that appears as a second line below the Hospital Name in printed reports. The subtitle can be configured in Settings and toggled on/off in the template builder's Header block. Print header structure: - Line 1: Hospital Name - Line 2: Subtitle (new, optional) - Line 3: Unit/Department
1 parent 168b523 commit 470e654

File tree

12 files changed

+142
-15
lines changed

12 files changed

+142
-15
lines changed

src/main/db/default-templates.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const defaultSurgeryTemplate = {
1515
type: 'header',
1616
props: {
1717
showHospital: true,
18+
showSubtitle: true,
1819
showUnit: true,
1920
showTelephone: true,
2021
showLogo: false,
@@ -193,6 +194,7 @@ export const defaultFollowupTemplate = {
193194
type: 'header',
194195
props: {
195196
showHospital: true,
197+
showSubtitle: true,
196198
showUnit: true,
197199
showTelephone: true,
198200
showLogo: false,

src/main/db/migrations.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import * as m_014_sync_default_templates from './migrations/014_sync_default_tem
1616
import * as m_015_add_discharge_plan from './migrations/015_add_discharge_plan'
1717
import * as m_016_update_default_templates_discharge_plan from './migrations/016_update_default_templates_discharge_plan'
1818
import * as m_017_sync_print_templates_with_defaults from './migrations/017_sync_print_templates_with_defaults'
19+
import * as m_018_subtitle_setting from './migrations/018_subtitle_setting'
1920

2021
export default {
2122
'000_init': m_000_init,
@@ -35,5 +36,6 @@ export default {
3536
'014_sync_default_templates': m_014_sync_default_templates,
3637
'015_add_discharge_plan': m_015_add_discharge_plan,
3738
'016_update_default_templates_discharge_plan': m_016_update_default_templates_discharge_plan,
38-
'017_sync_print_templates_with_defaults': m_017_sync_print_templates_with_defaults
39+
'017_sync_print_templates_with_defaults': m_017_sync_print_templates_with_defaults,
40+
'018_subtitle_setting': m_018_subtitle_setting
3941
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import { Kysely } from 'kysely'
3+
import { defaultSurgeryTemplate, defaultFollowupTemplate } from '../default-templates'
4+
5+
/**
6+
* Add subtitle support to print templates.
7+
*
8+
* This migration:
9+
* 1. Updates default_print_templates with the new template structure (includes showSubtitle prop)
10+
* 2. Syncs print_templates with the updated defaults
11+
*/
12+
export async function up(db: Kysely<any>): Promise<void> {
13+
const now = Date.now()
14+
15+
// Update default_print_templates with new template structures
16+
await db
17+
.updateTable('default_print_templates')
18+
.set({
19+
structure: JSON.stringify(defaultSurgeryTemplate)
20+
})
21+
.where('key', '=', 'surgery-standard')
22+
.execute()
23+
24+
await db
25+
.updateTable('default_print_templates')
26+
.set({
27+
structure: JSON.stringify(defaultFollowupTemplate)
28+
})
29+
.where('key', '=', 'followup-standard')
30+
.execute()
31+
32+
// Sync print_templates with updated defaults
33+
const defaults = await db
34+
.selectFrom('default_print_templates')
35+
.select(['key', 'type', 'structure'])
36+
.execute()
37+
38+
for (const defaultTemplate of defaults) {
39+
await db
40+
.updateTable('print_templates')
41+
.set({
42+
structure: defaultTemplate.structure,
43+
updated_at: now
44+
})
45+
.where('type', '=', defaultTemplate.type)
46+
.where('is_default', '=', 1)
47+
.execute()
48+
}
49+
}
50+
51+
export async function down(_db: Kysely<any>): Promise<void> {
52+
// No-op: This is a data sync migration, not reversible without storing old data
53+
}

src/renderer/src/components/settings/GeneralSettings.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type UpdateChannel = 'stable' | 'beta' | 'alpha'
3131

3232
const formSchema = z.object({
3333
hospital: z.string(),
34+
subtitle: z.string().optional(),
3435
unit: z.string(),
3536
telephone: z.string()
3637
})
@@ -44,6 +45,7 @@ export const GeneralSettings = () => {
4445
resolver: zodResolver(formSchema),
4546
defaultValues: {
4647
hospital: '',
48+
subtitle: '',
4749
unit: '',
4850
telephone: ''
4951
}
@@ -69,6 +71,7 @@ export const GeneralSettings = () => {
6971
if (settings) {
7072
form.reset({
7173
hospital: settings['hospital'] || '',
74+
subtitle: settings['subtitle'] || '',
7275
unit: settings['unit'] || '',
7376
telephone: settings['telephone'] || ''
7477
})
@@ -151,6 +154,32 @@ export const GeneralSettings = () => {
151154
)}
152155
/>
153156

157+
<FormField
158+
name="subtitle"
159+
control={form.control}
160+
render={({ field }) => (
161+
<FormItem>
162+
<div className="flex items-center gap-2 mb-2">
163+
<div className="h-6 w-6 rounded-md bg-blue-500/10 flex items-center justify-center">
164+
<Building2 className="h-3.5 w-3.5 text-blue-500" />
165+
</div>
166+
<FormLabel className="text-sm font-medium">Subtitle</FormLabel>
167+
</div>
168+
<FormControl>
169+
<Input
170+
placeholder="e.g., Teaching Hospital"
171+
{...field}
172+
className="h-11"
173+
/>
174+
</FormControl>
175+
<FormDescription className="text-xs">
176+
Optional second line shown below Hospital Name in printed reports
177+
</FormDescription>
178+
<FormMessage />
179+
</FormItem>
180+
)}
181+
/>
182+
154183
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
155184
<FormField
156185
name="unit"

src/renderer/src/components/template-builder/SampleDataContext.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,13 @@ export const SampleDataProvider = ({ templateType, children }: SampleDataProvide
9595
// Check if real settings are available
9696
const realSettings = useMemo(() => {
9797
const hospital = settings.hospital
98+
const subtitle = settings.subtitle
9899
const unit = settings.unit
99100
const telephone = settings.telephone
100101

101102
return {
102103
hospital: hospital || null,
104+
subtitle: subtitle || null,
103105
unit: unit || null,
104106
telephone: telephone || null,
105107
hasAny: Boolean(hospital || unit || telephone)
@@ -116,6 +118,7 @@ export const SampleDataProvider = ({ templateType, children }: SampleDataProvide
116118
...base,
117119
settings: {
118120
hospital: realSettings.hospital || base.settings.hospital,
121+
subtitle: realSettings.subtitle || base.settings.subtitle,
119122
unit: realSettings.unit || base.settings.unit,
120123
telephone: realSettings.telephone || base.settings.telephone
121124
}

src/renderer/src/components/template-builder/properties/HeaderProperties.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,17 @@ export const HeaderProperties = ({ block, onUpdate }: HeaderPropertiesProps) =>
3636
/>
3737
</div>
3838

39+
<div className="flex items-center justify-between">
40+
<Label htmlFor="showSubtitle" className="text-sm">
41+
Show Subtitle
42+
</Label>
43+
<Switch
44+
id="showSubtitle"
45+
checked={block.props.showSubtitle}
46+
onCheckedChange={(checked) => updateProps({ showSubtitle: checked })}
47+
/>
48+
</div>
49+
3950
<div className="flex items-center justify-between">
4051
<Label htmlFor="showUnit" className="text-sm">
4152
Show Unit

src/renderer/src/components/template-builder/sample-data/SettingsFields.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,34 @@ export const SettingsFields = () => {
1818
</div>
1919
)}
2020

21-
{/* Hospital, Unit */}
21+
{/* Hospital Name */}
22+
<FieldInput
23+
label="Hospital Name"
24+
value={settings.hospital}
25+
onChange={(v) => updateField('settings.hospital', v)}
26+
/>
27+
28+
{/* Subtitle */}
29+
<FieldInput
30+
label="Subtitle"
31+
value={settings.subtitle}
32+
onChange={(v) => updateField('settings.subtitle', v)}
33+
/>
34+
35+
{/* Unit, Telephone */}
2236
<div className="grid grid-cols-2 gap-2">
23-
<FieldInput
24-
label="Hospital Name"
25-
value={settings.hospital}
26-
onChange={(v) => updateField('settings.hospital', v)}
27-
/>
2837
<FieldInput
2938
label="Unit / Department"
3039
value={settings.unit}
3140
onChange={(v) => updateField('settings.unit', v)}
3241
/>
42+
<FieldInput
43+
label="Telephone"
44+
value={settings.telephone}
45+
onChange={(v) => updateField('settings.telephone', v)}
46+
/>
3347
</div>
3448

35-
{/* Telephone */}
36-
<FieldInput
37-
label="Telephone"
38-
value={settings.telephone}
39-
onChange={(v) => updateField('settings.telephone', v)}
40-
/>
41-
4249
{/* Note */}
4350
{!usingRealSettings && (
4451
<p className="text-[10px] text-muted-foreground pt-1">

src/renderer/src/lib/print.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const surgeryPrintData = (
4040
},
4141
settings: {
4242
hospital: settings?.hospital || '',
43+
subtitle: settings?.subtitle || '',
4344
unit: settings?.unit || '',
4445
telephone: settings?.telephone || ''
4546
}
@@ -70,6 +71,7 @@ export const followupPrintData = (
7071
},
7172
settings: {
7273
hospital: settings?.hospital || '',
74+
subtitle: settings?.subtitle || '',
7375
unit: settings?.unit || '',
7476
telephone: settings?.telephone || ''
7577
}
@@ -128,6 +130,7 @@ export const createSurgeryContext = (
128130
},
129131
settings: {
130132
hospital: settings?.hospital || 'Hospital Name',
133+
subtitle: settings?.subtitle || '',
131134
unit: settings?.unit || 'Unit Name',
132135
telephone: settings?.telephone || null
133136
}

src/renderer/src/lib/template-context.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const getSampleContext = (type: TemplateType): TemplateContext => {
6969
},
7070
settings: {
7171
hospital: 'General Hospital Colombo',
72+
subtitle: 'Teaching Hospital',
7273
unit: 'Surgical Unit A',
7374
telephone: '+94 11 234 5678'
7475
}
@@ -135,6 +136,7 @@ export interface CreateContextParams {
135136
}
136137
settings: {
137138
hospital?: string
139+
subtitle?: string
138140
unit?: string
139141
telephone?: string | null
140142
}
@@ -211,6 +213,7 @@ export const createTemplateContext = (params: CreateContextParams): TemplateCont
211213
: undefined,
212214
settings: {
213215
hospital: params.settings.hospital || 'Hospital Name',
216+
subtitle: params.settings.subtitle || '',
214217
unit: params.settings.unit || 'Unit Name',
215218
telephone: params.settings.telephone || null
216219
}

src/renderer/src/lib/template-renderer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ const renderHeader = (block: HeaderBlock, context: TemplateContext): string => {
9191
html += `<h1 class="text-2xl bold">${escapeHtml(context.settings.hospital)}</h1>`
9292
}
9393

94+
if (props.showSubtitle && context.settings.subtitle) {
95+
html += `<h2 class="text-xl pt-1">${escapeHtml(context.settings.subtitle)}</h2>`
96+
}
97+
9498
if (props.showUnit && context.settings.unit) {
9599
html += `<h2 class="text-lg pt-1">${escapeHtml(context.settings.unit)}</h2>`
96100
}

0 commit comments

Comments
 (0)