package schedule import ( "database/sql" "errors" "fmt" "time" ) type Schedule struct { ID int64 `json:"id"` VolunteerID int64 `json:"volunteer_id"` Title string `json:"title"` StartsAt time.Time `json:"starts_at"` EndsAt time.Time `json:"ends_at"` Notes string `json:"notes,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } type CreateInput struct { VolunteerID int64 `json:"volunteer_id"` Title string `json:"title"` StartsAt string `json:"starts_at"` EndsAt string `json:"ends_at"` Notes string `json:"notes"` } type UpdateInput struct { Title *string `json:"title"` StartsAt *string `json:"starts_at"` EndsAt *string `json:"ends_at"` Notes *string `json:"notes"` } const timeLayout = "2006-01-02T15:04:05Z" type Store struct { db *sql.DB } func NewStore(db *sql.DB) *Store { return &Store{db: db} } func (s *Store) Create(in CreateInput) (*Schedule, error) { res, err := s.db.Exec( `INSERT INTO schedules (volunteer_id, title, starts_at, ends_at, notes) VALUES (?, ?, ?, ?, ?)`, in.VolunteerID, in.Title, in.StartsAt, in.EndsAt, in.Notes, ) if err != nil { return nil, fmt.Errorf("insert schedule: %w", err) } id, _ := res.LastInsertId() return s.GetByID(id) } func (s *Store) GetByID(id int64) (*Schedule, error) { sc := &Schedule{} var startsAt, endsAt, createdAt, updatedAt string var notes sql.NullString err := s.db.QueryRow( `SELECT id, volunteer_id, title, starts_at, ends_at, notes, created_at, updated_at FROM schedules WHERE id = ?`, id, ).Scan(&sc.ID, &sc.VolunteerID, &sc.Title, &startsAt, &endsAt, ¬es, &createdAt, &updatedAt) if errors.Is(err, sql.ErrNoRows) { return nil, nil } if err != nil { return nil, fmt.Errorf("get schedule: %w", err) } sc.StartsAt, _ = time.Parse("2006-01-02 15:04:05", startsAt) sc.EndsAt, _ = time.Parse("2006-01-02 15:04:05", endsAt) sc.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt) sc.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt) if notes.Valid { sc.Notes = notes.String } return sc, nil } func (s *Store) List(volunteerID int64) ([]Schedule, error) { query := `SELECT id, volunteer_id, title, starts_at, ends_at, notes, created_at, updated_at FROM schedules` args := []any{} if volunteerID > 0 { query += ` WHERE volunteer_id = ?` args = append(args, volunteerID) } query += ` ORDER BY starts_at` rows, err := s.db.Query(query, args...) if err != nil { return nil, fmt.Errorf("list schedules: %w", err) } defer rows.Close() var schedules []Schedule for rows.Next() { var sc Schedule var startsAt, endsAt, createdAt, updatedAt string var notes sql.NullString if err := rows.Scan(&sc.ID, &sc.VolunteerID, &sc.Title, &startsAt, &endsAt, ¬es, &createdAt, &updatedAt); err != nil { return nil, err } sc.StartsAt, _ = time.Parse("2006-01-02 15:04:05", startsAt) sc.EndsAt, _ = time.Parse("2006-01-02 15:04:05", endsAt) sc.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt) sc.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt) if notes.Valid { sc.Notes = notes.String } schedules = append(schedules, sc) } return schedules, rows.Err() } func (s *Store) Update(id int64, in UpdateInput) (*Schedule, error) { sc, err := s.GetByID(id) if err != nil || sc == nil { return sc, err } title := sc.Title startsAt := sc.StartsAt.Format("2006-01-02 15:04:05") endsAt := sc.EndsAt.Format("2006-01-02 15:04:05") notes := sc.Notes if in.Title != nil { title = *in.Title } if in.StartsAt != nil { startsAt = *in.StartsAt } if in.EndsAt != nil { endsAt = *in.EndsAt } if in.Notes != nil { notes = *in.Notes } _, err = s.db.Exec( `UPDATE schedules SET title=?, starts_at=?, ends_at=?, notes=?, updated_at=datetime('now') WHERE id=?`, title, startsAt, endsAt, notes, id, ) if err != nil { return nil, fmt.Errorf("update schedule: %w", err) } return s.GetByID(id) } func (s *Store) Delete(id int64) error { _, err := s.db.Exec(`DELETE FROM schedules WHERE id = ?`, id) return err }