summaryrefslogtreecommitdiffstats
path: root/internal/admin/templates/form.html
blob: 4a93d3d4726cfae111d6199c41755c7dce4712ae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
{{define "form-content"}}
  <h1>{{.Title}}</h1>
  <form method="POST" action="{{.Action}}" id="postForm">
    <label for="title">Title</label>
    <input type="text" id="title" name="title" value="{{.Post.Title}}" required autofocus>

    <label for="slug">Slug</label>
    <input type="text" id="slug" name="slug" value="{{.Post.Slug}}" placeholder="auto-generated from title">
    <p class="hint">URL path for this post (e.g. "my-post" → /my-post). Lowercase letters, numbers, and hyphens only.</p>

    <label for="date">Date</label>
    <input type="date" id="date" name="date" value="{{.Post.Date}}">

    <label for="tags">Tags</label>
    <input type="text" id="tags" name="tags" value="{{range $i, $t := .Post.Tags}}{{if $i}}, {{end}}{{$t}}{{end}}" placeholder="tag1, tag2, tag3">
    <p class="hint">Comma-separated list of tags.</p>

    <label for="draft">
      <input type="checkbox" id="draft" name="draft"{{if .Post.Draft}} checked{{end}}>
      Draft (not published)
    </label>

    <label for="blocks">Content</label>
    <div id="editor" style="border: 1px solid #ccc; border-radius: 4px; min-height: 340px;"></div>
    <input type="hidden" id="blocks" name="blocks" value="">

    <div class="form-actions">
      <button type="submit" class="btn btn-primary">
        {{if .IsNew}}Create Post{{else}}Save Changes{{end}}
      </button>
      <a href="/admin/" class="btn btn-secondary">Cancel</a>
    </div>
  </form>

  <script type="module">
    import { EditorJS, Header, Paragraph, List, Code, Quote, ImageTool } from 'editorjs-bundle';
    import ComponentTool from '/assets/admin/lib/component-tool.js';

    const blocksField = document.getElementById('blocks');
    const form = document.getElementById('postForm');
    const titleInput = document.getElementById('title');
    const slugInput = document.getElementById('slug');

    // Auto-populate slug from title for new posts only, until user edits slug manually.
    {{if .IsNew}}
    let slugManuallyEdited = slugInput.value !== '';
    slugInput.addEventListener('input', () => { slugManuallyEdited = true; });
    titleInput.addEventListener('input', () => {
      if (slugManuallyEdited) return;
      slugInput.value = titleInput.value
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/^-+|-+$/g, '');
    });
    {{end}}

    let initialData = [];
    try {
      initialData = JSON.parse(blocksField.value || '[]');
    } catch (e) {
      console.error('Failed to parse blocks JSON:', e);
    }

    const editor = new EditorJS({
      holder: 'editor',
      tools: {
        header: Header,
        paragraph: Paragraph,
        list: List,
        code: Code,
        quote: Quote,
        image: {
          class: ImageTool,
          config: {
            endpoints: { byFile: '/admin/upload/image' },
          },
        },
        component: ComponentTool,
      },
      data: { blocks: initialData },
    });

    // Sync editor content back to hidden field before form submission
    form.addEventListener('submit', async (e) => {
      e.preventDefault();

      try {
        const editorData = await editor.save();
        blocksField.value = JSON.stringify(editorData);
      } catch (err) {
        console.error('Failed to save editor data:', err);
        return;
      }

      form.submit();
    });
  </script>
{{end}}