Compare commits
2 commits
740cf8979a
...
d9aec4af2c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9aec4af2c | ||
|
|
f45bdcd83c |
|
|
@ -10,21 +10,7 @@ $container-max-widths: (
|
||||||
|
|
||||||
@import "bootstrap/scss/bootstrap.scss";
|
@import "bootstrap/scss/bootstrap.scss";
|
||||||
|
|
||||||
// Local classes
|
// Post list
|
||||||
|
|
||||||
.post-form {
|
|
||||||
display: grid;
|
|
||||||
grid-template-rows: min-content auto min-content;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-form textarea {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.postlist .post img {
|
.postlist .post img {
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
|
|
@ -32,6 +18,49 @@ $container-max-widths: (
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.postlist .post-date {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post form
|
||||||
|
|
||||||
|
// Post edit page styling
|
||||||
|
.post-edit-page {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-edit-page main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-edit-page .post-form {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-edit-page .post-form .row {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-edit-page .post-form .col-md-9 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-edit-page .post-form textarea {
|
||||||
|
flex: 1;
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.show-upload figure img {
|
.show-upload figure img {
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ func (ph PostsHandler) New(c fiber.Ctx) error {
|
||||||
"post": p,
|
"post": p,
|
||||||
"categories": cats,
|
"categories": cats,
|
||||||
"selectedCategories": map[int64]bool{},
|
"selectedCategories": map[int64]bool{},
|
||||||
|
"bodyClass": "post-edit-page",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,6 +94,7 @@ func (ph PostsHandler) Edit(c fiber.Ctx) error {
|
||||||
"post": post,
|
"post": post,
|
||||||
"categories": cats,
|
"categories": cats,
|
||||||
"selectedCategories": selectedCategories,
|
"selectedCategories": selectedCategories,
|
||||||
|
"bodyClass": "post-edit-page",
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<h2>Categories</h2>
|
|
||||||
<ul>
|
|
||||||
{{ range .Categories }}
|
|
||||||
<li>
|
|
||||||
<a href="{{ url_abs .Path }}">{{ .Name }}</a> ({{ .PostCount }})
|
|
||||||
{{ if .DescriptionBrief }}<br><small>{{ .DescriptionBrief }}</small>{{ end }}
|
|
||||||
</li>
|
|
||||||
{{ end }}
|
|
||||||
</ul>
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<h2>{{ .Category.Name }}</h2>
|
|
||||||
{{ if .DescriptionHTML }}
|
|
||||||
<div>{{ .DescriptionHTML }}</div>
|
|
||||||
{{ end }}
|
|
||||||
{{ range .Posts }}
|
|
||||||
{{ if .Post.Title }}<h3>{{ .Post.Title }}</h3>{{ end }}
|
|
||||||
{{ .HTML }}
|
|
||||||
<a href="{{ url_abs .Path }}">{{ format_date .Post.PublishedAt }}</a>
|
|
||||||
{{ if .Categories }}
|
|
||||||
<p>
|
|
||||||
{{ range .Categories }}
|
|
||||||
<a href="{{ url_abs (printf "/categories/%s" .Slug) }}">{{ .Name }}</a>
|
|
||||||
{{ end }}
|
|
||||||
</p>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
@ -2,5 +2,6 @@ package simplecss
|
||||||
|
|
||||||
import "embed"
|
import "embed"
|
||||||
|
|
||||||
//go:embed *.html
|
//go:embed templates/*.html
|
||||||
|
//go:embed static/*
|
||||||
var FS embed.FS
|
var FS embed.FS
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
{{ range .Posts }}
|
|
||||||
{{ if .Post.Title }}<h3>{{ .Post.Title }}</h3>{{ end }}
|
|
||||||
{{ .HTML }}
|
|
||||||
<a href="{{ url_abs .Path }}">{{ format_date .Post.PublishedAt }}</a>
|
|
||||||
{{ if .Categories }}
|
|
||||||
<p>
|
|
||||||
{{ range .Categories }}
|
|
||||||
<a href="{{ url_abs (printf "/categories/%s" .Slug) }}">{{ .Name }}</a>
|
|
||||||
{{ end }}
|
|
||||||
</p>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
{{ if .Post.Title }}<h3>{{ .Post.Title }}</h3>{{ end }}
|
|
||||||
{{ .HTML }}
|
|
||||||
<a href="{{ url_abs .Path }}">{{ format_date .Post.PublishedAt }}</a>
|
|
||||||
{{ if .Categories }}
|
|
||||||
<p>
|
|
||||||
{{ range .Categories }}
|
|
||||||
<a href="{{ url_abs (printf "/categories/%s" .Slug) }}">{{ .Name }}</a>
|
|
||||||
{{ end }}
|
|
||||||
</p>
|
|
||||||
{{ end }}
|
|
||||||
55
layouts/simplecss/static/style.css
Normal file
55
layouts/simplecss/static/style.css
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
.h-entry {
|
||||||
|
margin-block-start: 1.5rem;
|
||||||
|
margin-block-end: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-meta a {
|
||||||
|
color: var(--text-light);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-meta a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-categories {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-categories a:before {
|
||||||
|
content: "#";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Category list */
|
||||||
|
|
||||||
|
ul.category-list {
|
||||||
|
list-style: none;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.category-list li {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
justify-content: start;
|
||||||
|
gap: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.category-list span.category-list-name {
|
||||||
|
min-width: 15vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Category single */
|
||||||
|
|
||||||
|
.category-description {
|
||||||
|
margin-block-start: 1.5rem;
|
||||||
|
margin-block-end: 2.5rem;
|
||||||
|
}
|
||||||
10
layouts/simplecss/templates/_post_meta.html
Normal file
10
layouts/simplecss/templates/_post_meta.html
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="post-meta">
|
||||||
|
<a href="{{ url_abs .Path }}">{{ format_date .Post.PublishedAt }}</a>
|
||||||
|
{{ if .Categories }}
|
||||||
|
<div class="post-categories">
|
||||||
|
{{ range .Categories }}
|
||||||
|
<a href="{{ url_abs (printf "/categories/%s" .Slug) }}">{{ .Name }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
9
layouts/simplecss/templates/categories_list.html
Normal file
9
layouts/simplecss/templates/categories_list.html
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<h2>Categories</h2>
|
||||||
|
<ul class="category-list">
|
||||||
|
{{ range .Categories }}
|
||||||
|
<li>
|
||||||
|
<span class="category-list-name"><a href="{{ url_abs .Path }}">{{ .Name }}</a> ({{ .PostCount }})</span>
|
||||||
|
{{ if .DescriptionBrief }}<small>{{ .DescriptionBrief }}</small>{{ end }}
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
11
layouts/simplecss/templates/categories_single.html
Normal file
11
layouts/simplecss/templates/categories_single.html
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<h2>{{ .Category.Name }}</h2>
|
||||||
|
{{ if .DescriptionHTML }}
|
||||||
|
<div class="notice category-description">{{ .DescriptionHTML }}</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ range .Posts }}
|
||||||
|
<div class="h-entry">
|
||||||
|
{{ if .Post.Title }}<h3>{{ .Post.Title }}</h3>{{ end }}
|
||||||
|
{{ .HTML }}
|
||||||
|
{{ template "_post_meta.html" . }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="{{ url_abs "/feed.xml" }}"/>
|
<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="{{ url_abs "/feed.xml" }}"/>
|
||||||
<link rel="alternate" type="application/json" title="JSON feed" href="{{ url_abs "/feed.json" }}"/>
|
<link rel="alternate" type="application/json" title="JSON feed" href="{{ url_abs "/feed.json" }}"/>
|
||||||
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
|
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
|
||||||
|
<link rel="stylesheet" href="{{ url_abs "/static/style.css" }}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
8
layouts/simplecss/templates/posts_list.html
Normal file
8
layouts/simplecss/templates/posts_list.html
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{{ range .Posts }}
|
||||||
|
<div class="h-entry">
|
||||||
|
{{ if .Post.Title }}<h3>{{ .Post.Title }}</h3>{{ end }}
|
||||||
|
{{ .HTML }}
|
||||||
|
|
||||||
|
{{ template "_post_meta.html" . }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
5
layouts/simplecss/templates/posts_single.html
Normal file
5
layouts/simplecss/templates/posts_single.html
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<div class="h-entry">
|
||||||
|
{{ if .Post.Title }}<h3>{{ .Post.Title }}</h3>{{ end }}
|
||||||
|
{{ .HTML }}
|
||||||
|
{{ template "_post_meta.html" . }}
|
||||||
|
</div>
|
||||||
|
|
@ -6,7 +6,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"iter"
|
"iter"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -31,11 +33,15 @@ type Builder struct {
|
||||||
func New(site pubmodel.Site, opts Options) (*Builder, error) {
|
func New(site pubmodel.Site, opts Options) (*Builder, error) {
|
||||||
tmpls, err := template.New("").
|
tmpls, err := template.New("").
|
||||||
Funcs(templateFns(site, opts)).
|
Funcs(templateFns(site, opts)).
|
||||||
ParseFS(opts.TemplatesFS, tmplNamePostSingle, tmplNamePostList, tmplNameLayoutMain, tmplNameCategoryList, tmplNameCategorySingle)
|
ParseFS(opts.TemplatesFS, "*.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, t := range tmpls.Templates() {
|
||||||
|
log.Printf("Loaded template %s", t.Name())
|
||||||
|
}
|
||||||
|
|
||||||
return &Builder{
|
return &Builder{
|
||||||
site: site,
|
site: site,
|
||||||
opts: opts,
|
opts: opts,
|
||||||
|
|
@ -109,6 +115,9 @@ func (b *Builder) BuildSite(outDir string) error {
|
||||||
return b.writeUploads(buildCtx, b.site.Uploads)
|
return b.writeUploads(buildCtx, b.site.Uploads)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Build static assets
|
||||||
|
eg.Go(func() error { return b.writeStaticAssets(buildCtx) })
|
||||||
|
|
||||||
return eg.Wait()
|
return eg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,10 +185,11 @@ func (b *Builder) renderFeeds(ctx buildContext, postIter iter.Seq[models.Maybe[*
|
||||||
}
|
}
|
||||||
|
|
||||||
feed.Items = append(feed.Items, &feedhub.Item{
|
feed.Items = append(feed.Items, &feedhub.Item{
|
||||||
Id: filepath.Join(b.site.BaseURL, post.GUID),
|
Id: filepath.Join(b.site.BaseURL, post.GUID),
|
||||||
Title: postTitle,
|
Title: postTitle,
|
||||||
Link: &feedhub.Link{Href: renderedPost.PostURL},
|
Link: &feedhub.Link{Href: renderedPost.PostURL},
|
||||||
Content: string(renderedPost.HTML),
|
Content: string(renderedPost.HTML),
|
||||||
|
// TO FIX: Why the heck does this only include the first category?
|
||||||
Category: catName,
|
Category: catName,
|
||||||
// TO FIX: Created should be first published
|
// TO FIX: Created should be first published
|
||||||
Created: post.PublishedAt,
|
Created: post.PublishedAt,
|
||||||
|
|
@ -431,7 +441,7 @@ func (b *Builder) renderTemplate(w io.Writer, name string, data interface{}) err
|
||||||
|
|
||||||
func (b *Builder) writeUploads(ctx buildContext, uploads []models.Upload) error {
|
func (b *Builder) writeUploads(ctx buildContext, uploads []models.Upload) error {
|
||||||
for _, u := range uploads {
|
for _, u := range uploads {
|
||||||
fullPath := filepath.Join(ctx.outDir, "uploads", u.Slug)
|
fullPath := filepath.Join(ctx.outDir, b.opts.BaseUploads, u.Slug)
|
||||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -459,3 +469,37 @@ func (b *Builder) writeUploads(ctx buildContext, uploads []models.Upload) error
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) writeStaticAssets(ctx buildContext) error {
|
||||||
|
return fs.WalkDir(b.opts.StaticFS, ".", func(path string, d os.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if d.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath := filepath.Join(ctx.outDir, b.opts.BaseStatic, path)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return func() error {
|
||||||
|
r, err := b.opts.StaticFS.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
w, err := os.Create(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(w, r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,17 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// BasePosts is the base path for posts.
|
BasePosts string // BasePosts is the base path for posts.
|
||||||
BasePosts string
|
BaseUploads string // BaseUploads is the base path for uploads.
|
||||||
|
BaseStatic string // BaseStatic is the base path for static assets.
|
||||||
|
|
||||||
// TemplatesFS provides the raw templates for rendering the site.
|
// TemplatesFS provides the raw templates for rendering the site.
|
||||||
TemplatesFS fs.FS
|
TemplatesFS fs.FS
|
||||||
|
|
||||||
|
// StaticFS provides the raw assets for the site. This will be written as is
|
||||||
|
// from the BaseStatic dir.
|
||||||
|
StaticFS fs.FS
|
||||||
|
|
||||||
// FeedItems holds the number of posts to show in the feed.
|
// FeedItems holds the number of posts to show in the feed.
|
||||||
FeedItems int
|
FeedItems int
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package publisher
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"iter"
|
"iter"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -102,9 +103,22 @@ func (p *Publisher) publishSite(ctx context.Context, pubSite pubmodel.Site, targ
|
||||||
renderTZ = time.UTC
|
renderTZ = time.UTC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
templateFS, err := fs.Sub(simplecss.FS, "templates")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
staticFS, err := fs.Sub(simplecss.FS, "static")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
sb, err := sitebuilder.New(pubSite, sitebuilder.Options{
|
sb, err := sitebuilder.New(pubSite, sitebuilder.Options{
|
||||||
BasePosts: "/posts",
|
BasePosts: "/posts",
|
||||||
TemplatesFS: simplecss.FS,
|
BaseUploads: "/uploads",
|
||||||
|
BaseStatic: "/static",
|
||||||
|
TemplatesFS: templateFS,
|
||||||
|
StaticFS: staticFS,
|
||||||
FeedItems: 30,
|
FeedItems: 30,
|
||||||
RenderTZ: renderTZ,
|
RenderTZ: renderTZ,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
<h4>{{ if .isNew }}New Category{{ else }}Edit Category{{ end }}</h4>
|
<h5>{{ if .isNew }}New Category{{ else }}Edit Category{{ end }}</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ if .isNew }}
|
{{ if .isNew }}
|
||||||
|
|
@ -10,27 +10,27 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<input type="hidden" name="guid" value="{{ .category.GUID }}">
|
<input type="hidden" name="guid" value="{{ .category.GUID }}">
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="catName" class="col-sm-2 col-form-label">Name</label>
|
<label for="catName" class="col-sm-3 col-form-label text-end">Name</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="text" class="form-control" id="catName" name="name" value="{{ .category.Name }}">
|
<input type="text" class="form-control" id="catName" name="name" value="{{ .category.Name }}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="catSlug" class="col-sm-2 col-form-label">Slug</label>
|
<label for="catSlug" class="col-sm-3 col-form-label text-end">Slug</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="text" class="form-control" id="catSlug" name="slug" value="{{ .category.Slug }}">
|
<input type="text" class="form-control" id="catSlug" name="slug" value="{{ .category.Slug }}">
|
||||||
<div class="form-text">Auto-generated from name if left blank.</div>
|
<div class="form-text">Auto-generated from name if left blank.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<label for="catDesc" class="col-sm-2 col-form-label">Description</label>
|
<label for="catDesc" class="col-sm-3 col-form-label text-end">Description</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea class="form-control" id="catDesc" name="description" rows="5">{{ .category.Description }}</textarea>
|
<textarea class="form-control" id="catDesc" name="description" rows="5">{{ .category.Description }}</textarea>
|
||||||
<div class="form-text">Markdown supported. Displayed on the category archive page.</div>
|
<div class="form-text">Markdown supported. Displayed on the category archive page.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-sm-2"></div>
|
<div class="col-sm-3"></div>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<button type="submit" class="btn btn-primary">{{ if .isNew }}Create{{ else }}Save{{ end }}</button>
|
<button type="submit" class="btn btn-primary">{{ if .isNew }}Create{{ else }}Save{{ end }}</button>
|
||||||
{{ if not .isNew }}
|
{{ if not .isNew }}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,32 @@
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<div class="my-4 d-flex justify-content-between align-items-baseline">
|
<div class="my-4 d-flex justify-content-between align-items-baseline">
|
||||||
<h4>Categories</h4>
|
|
||||||
<div>
|
<div>
|
||||||
<a href="/sites/{{ .site.ID }}/categories/new" class="btn btn-success">New Category</a>
|
<a href="/sites/{{ .site.ID }}/categories/new" class="btn btn-success">New Category</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="table">
|
{{ if .categories }}
|
||||||
<thead>
|
<table class="table">
|
||||||
<tr>
|
<thead>
|
||||||
<th>Name</th>
|
|
||||||
<th>Slug</th>
|
|
||||||
<th>Posts</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{ range .categories }}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/sites/{{ $.site.ID }}/categories/{{ .ID }}">{{ .Name }}</a></td>
|
<th>Name</th>
|
||||||
<td><code>{{ .Slug }}</code></td>
|
<th>Slug</th>
|
||||||
<td>{{ .PostCount }}</td>
|
<th>Posts</th>
|
||||||
<td>
|
|
||||||
<a href="/sites/{{ $.site.ID }}/categories/{{ .ID }}" class="btn btn-outline-secondary btn-sm">Edit</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{{ else }}
|
</thead>
|
||||||
<tr>
|
<tbody>
|
||||||
<td colspan="4" class="text-center text-muted py-4">No categories yet.</td>
|
{{ range .categories }}
|
||||||
</tr>
|
<tr>
|
||||||
{{ end }}
|
<td><a href="/sites/{{ $.site.ID }}/categories/{{ .ID }}">{{ .Name }}</a></td>
|
||||||
</tbody>
|
<td><code>{{ .Slug }}</code></td>
|
||||||
</table>
|
<td>{{ .PostCount }}</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{ else }}
|
||||||
|
<div class="h4 m-3 text-center">
|
||||||
|
<div class="position-absolute top-50 start-50 translate-middle">📚<br>No categories yet.</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<link rel="stylesheet" href="/static/assets/main.css">
|
<link rel="stylesheet" href="/static/assets/main.css">
|
||||||
<script src="/static/assets/main.js" type="module"></script>
|
<script src="/static/assets/main.js" type="module"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-vh-100 d-flex flex-column">
|
<body class="d-flex flex-column {{.bodyClass}}">
|
||||||
{{ template "_common/nav" . }}
|
{{ template "_common/nav" . }}
|
||||||
|
|
||||||
{{ embed }}
|
{{ embed }}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{{ $isPublished := ne .post.State 1 }}
|
{{ $isPublished := ne .post.State 1 }}
|
||||||
<main class="flex-grow-1 position-relative">
|
<main class="flex-grow-1 position-relative">
|
||||||
<form action="/sites/{{.site.ID}}/posts" method="post" class="container-fluid post-form p-2"
|
<form action="/sites/{{.site.ID}}/posts" method="post" class="container-fluid post-form py-2"
|
||||||
data-controller="postedit"
|
data-controller="postedit"
|
||||||
data-action="keydown.meta+s->postedit#save keydown.meta+enter->postedit#publish"
|
data-action="keydown.meta+s->postedit#save keydown.meta+enter->postedit#publish"
|
||||||
data-postedit-save-action-value="{{ if $isPublished }}Update{{ else }}Save Draft{{ end }}">
|
data-postedit-save-action-value="{{ if $isPublished }}Update{{ else }}Save Draft{{ end }}">
|
||||||
|
|
@ -10,9 +10,7 @@
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<input type="text" name="title" class="form-control" placeholder="Title" value="{{ .post.Title }}">
|
<input type="text" name="title" class="form-control" placeholder="Title" value="{{ .post.Title }}">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<textarea data-postedit-target="bodyTextEdit" name="body" class="form-control flex-grow-1" rows="3">{{.post.Body}}</textarea>
|
||||||
<textarea data-postedit-target="bodyTextEdit" name="body" class="form-control" rows="3">{{.post.Body}}</textarea>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
{{ if $isPublished }}
|
{{ if $isPublished }}
|
||||||
<input type="submit" name="action" class="btn btn-primary mt-2" value="Update">
|
<input type="submit" name="action" class="btn btn-primary mt-2" value="Update">
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,9 @@
|
||||||
|
|
||||||
<div class="mb-3 d-flex align-items-center flex-wrap gap-1">
|
<div class="mb-3 d-flex align-items-center flex-wrap gap-1">
|
||||||
{{ if eq $p.State 1 }}
|
{{ if eq $p.State 1 }}
|
||||||
<span class="text-muted">{{ $.user.FormatTime $p.UpdatedAt }}</span> <span class="ms-3 badge text-primary-emphasis bg-primary-subtle border border-primary-subtle">Draft</span>
|
<span class="text-muted post-date">{{ $.user.FormatTime $p.UpdatedAt }}</span> <span class="ms-3 badge text-primary-emphasis bg-primary-subtle border border-primary-subtle">Draft</span>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<span class="text-muted">{{ $.user.FormatTime $p.PublishedAt }}</span>
|
<span class="text-muted post-date">{{ $.user.FormatTime $p.PublishedAt }}</span>
|
||||||
{{ end }}
|
|
||||||
{{ range $p.Categories }}
|
|
||||||
<span class="ms-1 badge bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle">{{ .Name }}</span>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue