Skip to content

Commit 69019d2

Browse files
makingclaude
andcommitted
Add Load Template feature to entry form
Allow users to load a template when creating new entries by clicking the Load Template button in the create entry form. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 65475ff commit 69019d2

File tree

4 files changed

+83
-4
lines changed

4 files changed

+83
-4
lines changed

src/test/java/am/ik/blog/e2e/ConsoleE2ETest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,23 @@ void autoGenerateSummaryFailureShowsErrorWithStatusCode() {
297297
formPage.verifyErrorContainsStatusCode(500);
298298
}
299299

300+
@Test
301+
void loadTemplateInCreateMode() {
302+
// Login and navigate to create entry form
303+
EntryListPage entryListPage = login();
304+
entryListPage.waitForLoad();
305+
306+
EntryFormPage formPage = entryListPage.clickNewEntry();
307+
formPage.waitForLoad();
308+
formPage.verifyCreatePageDisplayed();
309+
310+
// Click Load Template button
311+
formPage.clickLoadTemplate();
312+
313+
// Verify the form is populated with template data
314+
formPage.verifyTitle("How to Build a REST API with Spring Boot");
315+
}
316+
300317
void setupOpenAiMock(String summaryText) {
301318
String sseResponse = """
302319
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1234567890,"model":"gpt-4o-mini","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

src/test/java/am/ik/blog/e2e/page/EntryFormPage.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,11 @@ public EntryFormPage fillEntryId(String entryId) {
119119
* Click the Load button to load an existing entry.
120120
*/
121121
public EntryFormPage clickLoad() {
122-
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Load")).click();
122+
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Load").setExact(true)).click();
123123
// Wait for loading to complete (button becomes enabled again)
124-
page.locator("button:has-text('Load'):not([disabled])").waitFor();
124+
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Load").setExact(true))
125+
.and(page.locator(":not([disabled])"))
126+
.waitFor();
125127
return this;
126128
}
127129

@@ -143,6 +145,16 @@ public EntryFormPage loadExistingEntry(String entryId) {
143145
return clickLoad();
144146
}
145147

148+
/**
149+
* Click the Load Template button to load the template.
150+
*/
151+
public EntryFormPage clickLoadTemplate() {
152+
page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Load Template")).click();
153+
// Wait for loading to complete (button becomes enabled again)
154+
page.locator("button:has-text('Load Template'):not([disabled])").waitFor();
155+
return this;
156+
}
157+
146158
/**
147159
* Click the Preview & Create button.
148160
*/

ui/src/pages/entries/EntryForm.tsx

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ export function EntryForm({ mode }: EntryFormProps) {
3838
const [isRestoredFromDraft, setIsRestoredFromDraft] = useState(false);
3939
const [isLoadingExisting, setIsLoadingExisting] = useState(false);
4040
const [isSummarizing, setIsSummarizing] = useState(false);
41+
const [isLoadingTemplate, setIsLoadingTemplate] = useState(false);
4142

42-
const isFormBusy = isLoadingExisting || isSummarizing;
43+
const isFormBusy = isLoadingExisting || isSummarizing || isLoadingTemplate;
4344

4445
// Load existing entry for edit mode
4546
const {
@@ -286,6 +287,33 @@ export function EntryForm({ mode }: EntryFormProps) {
286287
}
287288
};
288289

290+
const handleLoadTemplate = async () => {
291+
setIsLoadingTemplate(true);
292+
setSubmitError(null);
293+
294+
try {
295+
const templateMarkdown = await api.getTemplate();
296+
const { frontMatter, content } = parseMarkdownWithFrontMatter(templateMarkdown);
297+
setFormData({
298+
title: frontMatter.title || '',
299+
summary: frontMatter.summary || '',
300+
categories: frontMatter.categories?.map(c => c.name) || [],
301+
tags: frontMatter.tags?.map(t => t.name) || [],
302+
content: content || '',
303+
});
304+
setMarkdownContent(templateMarkdown);
305+
} catch (error) {
306+
if (error instanceof ApiError) {
307+
const detail = error.problemDetail?.detail || error.message;
308+
setSubmitError(`HTTP ${error.status}: ${detail}`);
309+
} else {
310+
setSubmitError(error instanceof Error ? error.message : 'Failed to load template');
311+
}
312+
} finally {
313+
setIsLoadingTemplate(false);
314+
}
315+
};
316+
289317
const handleSubmit = (e: React.FormEvent) => {
290318
e.preventDefault();
291319
setSubmitError(null);
@@ -474,7 +502,21 @@ export function EntryForm({ mode }: EntryFormProps) {
474502
<>
475503
{/* Basic Information */}
476504
<div className="border border-gray-200 p-4">
477-
<h3 className="text-base font-medium text-black mb-4">Basic Information</h3>
505+
<div className="flex items-center justify-between mb-4">
506+
<h3 className="text-base font-medium text-black">Basic Information</h3>
507+
{mode === 'create' && (
508+
<Button
509+
type="button"
510+
variant="secondary"
511+
size="sm"
512+
onClick={() => void handleLoadTemplate()}
513+
disabled={isFormBusy}
514+
loading={isLoadingTemplate}
515+
>
516+
Load Template
517+
</Button>
518+
)}
519+
</div>
478520
<div className="space-y-4">
479521
<Input
480522
label="Title *"

ui/src/services/api.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,14 @@ export const api = {
237237
const result = await handleResponse<{ summary: string }>(response);
238238
return result.summary;
239239
},
240+
241+
// Template operations
242+
async getTemplate(): Promise<string> {
243+
const response = await fetch('/entries/template.md', {
244+
headers: buildHeaders(),
245+
});
246+
return handleResponse<string>(response);
247+
},
240248
};
241249

242250
export { ApiError };

0 commit comments

Comments
 (0)