Fix time-off date display and improve UI
Scan datetime columns directly into time.Time instead of strings in the timeoff store — the intermediate string parse silently failed with parseTime=true, producing zero-value dates. Display dates with month names and filter admin's own entry from the volunteer dropdown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -80,25 +80,20 @@ func (s *Store) Create(ctx context.Context, volunteerID int64, in CreateInput) (
|
||||
|
||||
func (s *Store) GetByID(ctx context.Context, id int64) (*Request, error) {
|
||||
req := &Request{}
|
||||
var startsAt, endsAt, createdAt, updatedAt string
|
||||
var reason sql.NullString
|
||||
var reviewedBy sql.NullInt64
|
||||
var reviewedAt sql.NullString
|
||||
var reviewedAt sql.NullTime
|
||||
|
||||
err := s.db.QueryRowContext(ctx,
|
||||
`SELECT id, volunteer_id, starts_at, ends_at, reason, status, reviewed_by, reviewed_at, created_at, updated_at
|
||||
FROM time_off_requests WHERE id = ?`, id,
|
||||
).Scan(&req.ID, &req.VolunteerID, &startsAt, &endsAt, &reason, &req.Status, &reviewedBy, &reviewedAt, &createdAt, &updatedAt)
|
||||
).Scan(&req.ID, &req.VolunteerID, &req.StartsAt, &req.EndsAt, &reason, &req.Status, &reviewedBy, &reviewedAt, &req.CreatedAt, &req.UpdatedAt)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get time off request: %w", err)
|
||||
}
|
||||
req.StartsAt, _ = time.Parse("2006-01-02 15:04:05", startsAt)
|
||||
req.EndsAt, _ = time.Parse("2006-01-02 15:04:05", endsAt)
|
||||
req.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
|
||||
req.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt)
|
||||
if reason.Valid {
|
||||
req.Reason = reason.String
|
||||
}
|
||||
@@ -106,8 +101,7 @@ func (s *Store) GetByID(ctx context.Context, id int64) (*Request, error) {
|
||||
req.ReviewedBy = &reviewedBy.Int64
|
||||
}
|
||||
if reviewedAt.Valid {
|
||||
t, _ := time.Parse("2006-01-02 15:04:05", reviewedAt.String)
|
||||
req.ReviewedAt = &t
|
||||
req.ReviewedAt = &reviewedAt.Time
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
@@ -130,17 +124,12 @@ func (s *Store) List(ctx context.Context, volunteerID int64) ([]Request, error)
|
||||
var requests []Request
|
||||
for rows.Next() {
|
||||
var req Request
|
||||
var startsAt, endsAt, createdAt, updatedAt string
|
||||
var reason sql.NullString
|
||||
var reviewedBy sql.NullInt64
|
||||
var reviewedAt sql.NullString
|
||||
if err := rows.Scan(&req.ID, &req.VolunteerID, &startsAt, &endsAt, &reason, &req.Status, &reviewedBy, &reviewedAt, &createdAt, &updatedAt); err != nil {
|
||||
var reviewedAt sql.NullTime
|
||||
if err := rows.Scan(&req.ID, &req.VolunteerID, &req.StartsAt, &req.EndsAt, &reason, &req.Status, &reviewedBy, &reviewedAt, &req.CreatedAt, &req.UpdatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.StartsAt, _ = time.Parse("2006-01-02 15:04:05", startsAt)
|
||||
req.EndsAt, _ = time.Parse("2006-01-02 15:04:05", endsAt)
|
||||
req.CreatedAt, _ = time.Parse("2006-01-02 15:04:05", createdAt)
|
||||
req.UpdatedAt, _ = time.Parse("2006-01-02 15:04:05", updatedAt)
|
||||
if reason.Valid {
|
||||
req.Reason = reason.String
|
||||
}
|
||||
@@ -148,8 +137,7 @@ func (s *Store) List(ctx context.Context, volunteerID int64) ([]Request, error)
|
||||
req.ReviewedBy = &reviewedBy.Int64
|
||||
}
|
||||
if reviewedAt.Valid {
|
||||
t, _ := time.Parse("2006-01-02 15:04:05", reviewedAt.String)
|
||||
req.ReviewedAt = &t
|
||||
req.ReviewedAt = &reviewedAt.Time
|
||||
}
|
||||
requests = append(requests, req)
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ export default function TimeOff() {
|
||||
onChange={e => setForm(f => ({ ...f, volunteer_id: Number(e.target.value) }))}
|
||||
>
|
||||
<option value={0}>Myself</option>
|
||||
{volunteers.map(v => (
|
||||
{volunteers.filter(v => v.id !== volunteerID).map(v => (
|
||||
<option key={v.id} value={v.id}>{v.name}</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -226,8 +226,8 @@ export default function TimeOff() {
|
||||
{requests.map(r => (
|
||||
<tr key={r.id}>
|
||||
{role === 'admin' && <td>{volunteerName(r.volunteer_id)}</td>}
|
||||
<td>{new Date(r.starts_at).toLocaleDateString()}</td>
|
||||
<td>{new Date(r.ends_at).toLocaleDateString()}</td>
|
||||
<td>{new Date(r.starts_at).toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}</td>
|
||||
<td>{new Date(r.ends_at).toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}</td>
|
||||
<td>{r.reason ?? '—'}</td>
|
||||
<td><span className={statusClass(r.status)}>{r.status}</span></td>
|
||||
<td>
|
||||
|
||||
Reference in New Issue
Block a user