Implement time off management (Issue #3) #12

Merged
thatguygriff merged 3 commits from feature/time-off-management into main 2026-04-09 19:09:17 +00:00
2 changed files with 9 additions and 21 deletions
Showing only changes of commit 975225e650 - Show all commits

View File

@@ -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) { func (s *Store) GetByID(ctx context.Context, id int64) (*Request, error) {
req := &Request{} req := &Request{}
var startsAt, endsAt, createdAt, updatedAt string
var reason sql.NullString var reason sql.NullString
var reviewedBy sql.NullInt64 var reviewedBy sql.NullInt64
var reviewedAt sql.NullString var reviewedAt sql.NullTime
err := s.db.QueryRowContext(ctx, err := s.db.QueryRowContext(ctx,
`SELECT id, volunteer_id, starts_at, ends_at, reason, status, reviewed_by, reviewed_at, created_at, updated_at `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, 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) { if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNotFound return nil, ErrNotFound
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("get time off request: %w", err) 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 { if reason.Valid {
req.Reason = reason.String req.Reason = reason.String
} }
@@ -106,8 +101,7 @@ func (s *Store) GetByID(ctx context.Context, id int64) (*Request, error) {
req.ReviewedBy = &reviewedBy.Int64 req.ReviewedBy = &reviewedBy.Int64
} }
if reviewedAt.Valid { if reviewedAt.Valid {
t, _ := time.Parse("2006-01-02 15:04:05", reviewedAt.String) req.ReviewedAt = &reviewedAt.Time
req.ReviewedAt = &t
} }
return req, nil return req, nil
} }
@@ -130,17 +124,12 @@ func (s *Store) List(ctx context.Context, volunteerID int64) ([]Request, error)
var requests []Request var requests []Request
for rows.Next() { for rows.Next() {
var req Request var req Request
var startsAt, endsAt, createdAt, updatedAt string
var reason sql.NullString var reason sql.NullString
var reviewedBy sql.NullInt64 var reviewedBy sql.NullInt64
var reviewedAt sql.NullString var reviewedAt sql.NullTime
if err := rows.Scan(&req.ID, &req.VolunteerID, &startsAt, &endsAt, &reason, &req.Status, &reviewedBy, &reviewedAt, &createdAt, &updatedAt); err != nil { 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 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 { if reason.Valid {
req.Reason = reason.String req.Reason = reason.String
} }
@@ -148,8 +137,7 @@ func (s *Store) List(ctx context.Context, volunteerID int64) ([]Request, error)
req.ReviewedBy = &reviewedBy.Int64 req.ReviewedBy = &reviewedBy.Int64
} }
if reviewedAt.Valid { if reviewedAt.Valid {
t, _ := time.Parse("2006-01-02 15:04:05", reviewedAt.String) req.ReviewedAt = &reviewedAt.Time
req.ReviewedAt = &t
} }
requests = append(requests, req) requests = append(requests, req)
} }

View File

@@ -157,7 +157,7 @@ export default function TimeOff() {
onChange={e => setForm(f => ({ ...f, volunteer_id: Number(e.target.value) }))} onChange={e => setForm(f => ({ ...f, volunteer_id: Number(e.target.value) }))}
> >
<option value={0}>Myself</option> <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> <option key={v.id} value={v.id}>{v.name}</option>
))} ))}
</select> </select>
@@ -226,8 +226,8 @@ export default function TimeOff() {
{requests.map(r => ( {requests.map(r => (
<tr key={r.id}> <tr key={r.id}>
{role === 'admin' && <td>{volunteerName(r.volunteer_id)}</td>} {role === 'admin' && <td>{volunteerName(r.volunteer_id)}</td>}
<td>{new Date(r.starts_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()}</td> <td>{new Date(r.ends_at).toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })}</td>
<td>{r.reason ?? '—'}</td> <td>{r.reason ?? '—'}</td>
<td><span className={statusClass(r.status)}>{r.status}</span></td> <td><span className={statusClass(r.status)}>{r.status}</span></td>
<td> <td>