Adopt SummerCMS v0.1.1 admin features

Datepickers on native time.Time / lagoon.Date fields, file uploads on
materials, readings and class sessions managed from the course form
(course_id made nullable).
This commit is contained in:
Jakub Zych
2026-10-03 06:03:34 +02:00
parent ac2e2ddd7b
commit 71b848a376
39 changed files with 475 additions and 373 deletions

View File

@@ -9,6 +9,7 @@ import (
"io"
"slices"
"strings"
"time"
"git.golem15.com/golem15/summercms/modules/lagoon"
"git.golem15.com/jakub/sm-bm-plugin/models"
@@ -191,7 +192,7 @@ func (f *SeedFile) Validate() error {
if err2 != nil {
add("%s: ends_on: %v", at, err2)
}
if err1 == nil && err2 == nil && end.Before(start) {
if err1 == nil && err2 == nil && end.Time(nil).Before(start.Time(nil)) {
add("%s: ends_on is before starts_on", at)
}
}
@@ -314,10 +315,8 @@ func SeedWith(ctx context.Context, db *gorm.DB, f *SeedFile, opts SeedOptions) (
m := models.Meeting{}
created, changed, err := upsert(tx, &m, clause.Eq{Column: "number", Value: in.Number}, func(m *models.Meeting) {
m.Number = in.Number
start, _ := models.ParseDate(in.StartsOn)
end, _ := models.ParseDate(in.EndsOn)
m.StartsOn = models.NewDate(start)
m.EndsOn = models.NewDate(end)
m.StartsOn, _ = models.ParseDate(in.StartsOn)
m.EndsOn, _ = models.ParseDate(in.EndsOn)
m.Notes = nz(in.Notes)
})
if err != nil {
@@ -366,15 +365,16 @@ func SeedWith(ctx context.Context, db *gorm.DB, f *SeedFile, opts SeedOptions) (
clause.Eq{Column: "starts_at", Value: start.UTC()},
)
created, changed, err := upsert(tx, &s, where, func(s *models.ClassSession) {
s.CourseID = courseIDs[in.Course]
courseID := courseIDs[in.Course]
s.CourseID = &courseID
s.MeetingID = nil
if in.Meeting != nil {
id := meetingIDs[*in.Meeting]
s.MeetingID = &id
}
s.StartsAt = models.NewDateTime(start)
end, _ := models.ParseDateTime(in.EndsAt)
s.EndsAt = models.NewDateTime(end)
setInstant(&s.StartsAt, start)
setInstant(&s.EndsAt, end)
s.Room = nz(in.Room)
s.Kind = nz(in.Kind)
s.IsOnline = in.IsOnline
@@ -396,7 +396,7 @@ func SeedWith(ctx context.Context, db *gorm.DB, f *SeedFile, opts SeedOptions) (
)
created, changed, err := upsert(tx, &d, where, func(d *models.Deadline) {
d.Title = in.Title
d.DueAt = models.NewDateTime(due)
setInstant(&d.DueAt, due)
d.CourseID = nil
if in.Course != nil {
id := courseIDs[*in.Course]
@@ -419,7 +419,8 @@ func SeedWith(ctx context.Context, db *gorm.DB, f *SeedFile, opts SeedOptions) (
clause.Eq{Column: "title", Value: in.Title},
)
created, changed, err := upsert(tx, &r, where, func(r *models.Reading) {
r.CourseID = courseIDs[in.Course]
courseID := courseIDs[in.Course]
r.CourseID = &courseID
r.Title = in.Title
r.Author = nz(in.Author)
r.Source = nz(in.Source)
@@ -486,7 +487,7 @@ func prune(tx *gorm.DB, sum *SeedSummary, semesters map[int]bool, courseIDs map[
return err
}
for _, s := range sessions {
sum.Removed = append(sum.Removed, fmt.Sprintf("class session of course %d at %s", s.CourseID, string(s.StartsAt)))
sum.Removed = append(sum.Removed, fmt.Sprintf("class session of course %d at %s", s.CourseID, models.FormatDateTime(s.StartsAt)))
if err := tx.Delete(&models.ClassSession{}, s.ID).Error; err != nil {
return fmt.Errorf("prune class session %d: %w", s.ID, err)
}
@@ -520,6 +521,15 @@ func nz(s *string) *string {
// an insert when there was no row, an update when set changed a column, and
// nothing otherwise. The comparison is on the row's own fields, before and
// after set, as JSON.
// setInstant sets *dst to t unless it already holds the same instant, so a
// time read back from the database in another zone does not count as a
// change in upsert.
func setInstant(dst *time.Time, t time.Time) {
if !dst.Equal(t) {
*dst = t
}
}
func upsert[T any](tx *gorm.DB, row *T, where clause.Expression, set func(*T)) (created, changed bool, err error) {
res := tx.Clauses(where).Limit(1).Find(row)
if res.Error != nil {