first commit
This commit is contained in:
329
backend/internal/calendar/items.go
Normal file
329
backend/internal/calendar/items.go
Normal file
@@ -0,0 +1,329 @@
|
||||
package calendar
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
var ErrCalendarItemNotFound = errors.New("calendar item not found")
|
||||
|
||||
type CalendarItem struct {
|
||||
ID string `json:"id"`
|
||||
ClientID string `json:"client_id"`
|
||||
ClientName string `json:"client_name"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
ContentType string `json:"content_type"`
|
||||
Status string `json:"status"`
|
||||
OwnerID *string `json:"owner_id"`
|
||||
ScheduledDate string `json:"scheduled_date"`
|
||||
ScheduledAt *time.Time `json:"scheduled_at"`
|
||||
CopyText string `json:"copy_text"`
|
||||
InternalNotes string `json:"internal_notes"`
|
||||
ClientNotes string `json:"client_notes"`
|
||||
CreatedBy *string `json:"created_by"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type ListCalendarItemsFilter struct {
|
||||
ClientID string
|
||||
ViewerUserID string
|
||||
From string
|
||||
To string
|
||||
}
|
||||
|
||||
func (s Store) ListCalendarItems(ctx context.Context, filter ListCalendarItemsFilter) ([]CalendarItem, error) {
|
||||
rows, err := s.db.Query(ctx, `
|
||||
SELECT
|
||||
calendar_items.id::text,
|
||||
calendar_items.client_id::text,
|
||||
clients.name,
|
||||
calendar_items.title,
|
||||
calendar_items.description,
|
||||
calendar_items.content_type,
|
||||
calendar_items.status,
|
||||
calendar_items.owner_id::text,
|
||||
calendar_items.scheduled_date::text,
|
||||
calendar_items.scheduled_at,
|
||||
calendar_items.copy_text,
|
||||
calendar_items.internal_notes,
|
||||
calendar_items.client_notes,
|
||||
calendar_items.created_by::text,
|
||||
calendar_items.created_at,
|
||||
calendar_items.updated_at
|
||||
FROM calendar_items
|
||||
INNER JOIN clients ON clients.id = calendar_items.client_id
|
||||
WHERE calendar_items.scheduled_date >= $1::date
|
||||
AND calendar_items.scheduled_date <= $2::date
|
||||
AND ($3 = '' OR calendar_items.client_id::text = $3)
|
||||
AND (
|
||||
$4 = ''
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM client_users
|
||||
WHERE client_users.client_id = calendar_items.client_id
|
||||
AND client_users.user_id = $4::uuid
|
||||
)
|
||||
)
|
||||
ORDER BY calendar_items.scheduled_date ASC, calendar_items.created_at ASC
|
||||
`, filter.From, filter.To, filter.ClientID, filter.ViewerUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
items := []CalendarItem{}
|
||||
for rows.Next() {
|
||||
item, err := scanCalendarItem(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items, rows.Err()
|
||||
}
|
||||
|
||||
func (s Store) FindCalendarItem(ctx context.Context, id string, viewerUserID string) (CalendarItem, error) {
|
||||
row := s.db.QueryRow(ctx, `
|
||||
SELECT
|
||||
calendar_items.id::text,
|
||||
calendar_items.client_id::text,
|
||||
clients.name,
|
||||
calendar_items.title,
|
||||
calendar_items.description,
|
||||
calendar_items.content_type,
|
||||
calendar_items.status,
|
||||
calendar_items.owner_id::text,
|
||||
calendar_items.scheduled_date::text,
|
||||
calendar_items.scheduled_at,
|
||||
calendar_items.copy_text,
|
||||
calendar_items.internal_notes,
|
||||
calendar_items.client_notes,
|
||||
calendar_items.created_by::text,
|
||||
calendar_items.created_at,
|
||||
calendar_items.updated_at
|
||||
FROM calendar_items
|
||||
INNER JOIN clients ON clients.id = calendar_items.client_id
|
||||
WHERE calendar_items.id = $1::uuid
|
||||
AND (
|
||||
$2 = ''
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM client_users
|
||||
WHERE client_users.client_id = calendar_items.client_id
|
||||
AND client_users.user_id = $2::uuid
|
||||
)
|
||||
)
|
||||
`, id, viewerUserID)
|
||||
|
||||
return scanCalendarItem(row)
|
||||
}
|
||||
|
||||
type CreateCalendarItemInput struct {
|
||||
ClientID string
|
||||
Title string
|
||||
Description string
|
||||
ContentType string
|
||||
Status string
|
||||
ScheduledDate string
|
||||
CopyText string
|
||||
InternalNotes string
|
||||
ClientNotes string
|
||||
CreatedBy string
|
||||
}
|
||||
|
||||
func (s Store) CreateCalendarItem(ctx context.Context, input CreateCalendarItemInput) (CalendarItem, error) {
|
||||
row := s.db.QueryRow(ctx, `
|
||||
INSERT INTO calendar_items (
|
||||
client_id,
|
||||
title,
|
||||
description,
|
||||
content_type,
|
||||
status,
|
||||
scheduled_date,
|
||||
copy_text,
|
||||
internal_notes,
|
||||
client_notes,
|
||||
created_by
|
||||
)
|
||||
VALUES ($1::uuid, $2, $3, $4, $5, $6::date, $7, $8, $9, NULLIF($10, '')::uuid)
|
||||
RETURNING
|
||||
calendar_items.id::text,
|
||||
calendar_items.client_id::text,
|
||||
(SELECT name FROM clients WHERE clients.id = calendar_items.client_id),
|
||||
calendar_items.title,
|
||||
calendar_items.description,
|
||||
calendar_items.content_type,
|
||||
calendar_items.status,
|
||||
calendar_items.owner_id::text,
|
||||
calendar_items.scheduled_date::text,
|
||||
calendar_items.scheduled_at,
|
||||
calendar_items.copy_text,
|
||||
calendar_items.internal_notes,
|
||||
calendar_items.client_notes,
|
||||
calendar_items.created_by::text,
|
||||
calendar_items.created_at,
|
||||
calendar_items.updated_at
|
||||
`,
|
||||
input.ClientID,
|
||||
input.Title,
|
||||
input.Description,
|
||||
input.ContentType,
|
||||
input.Status,
|
||||
input.ScheduledDate,
|
||||
input.CopyText,
|
||||
input.InternalNotes,
|
||||
input.ClientNotes,
|
||||
input.CreatedBy,
|
||||
)
|
||||
|
||||
return scanCalendarItem(row)
|
||||
}
|
||||
|
||||
type UpdateCalendarItemInput struct {
|
||||
ID string
|
||||
Title string
|
||||
UpdateTitle bool
|
||||
Description string
|
||||
UpdateDescription bool
|
||||
Status string
|
||||
UpdateStatus bool
|
||||
ScheduledDate string
|
||||
UpdateScheduledDate bool
|
||||
CopyText string
|
||||
UpdateCopyText bool
|
||||
InternalNotes string
|
||||
UpdateInternalNotes bool
|
||||
ClientNotes string
|
||||
UpdateClientNotes bool
|
||||
}
|
||||
|
||||
func (s Store) UpdateCalendarItem(ctx context.Context, input UpdateCalendarItemInput) (CalendarItem, error) {
|
||||
row := s.db.QueryRow(ctx, `
|
||||
UPDATE calendar_items
|
||||
SET
|
||||
title = CASE WHEN $2 THEN $3 ELSE title END,
|
||||
description = CASE WHEN $4 THEN $5 ELSE description END,
|
||||
status = CASE WHEN $6 THEN $7 ELSE status END,
|
||||
scheduled_date = CASE WHEN $8 THEN NULLIF($9, '')::date ELSE scheduled_date END,
|
||||
copy_text = CASE WHEN $10 THEN $11 ELSE copy_text END,
|
||||
internal_notes = CASE WHEN $12 THEN $13 ELSE internal_notes END,
|
||||
client_notes = CASE WHEN $14 THEN $15 ELSE client_notes END,
|
||||
updated_at = now()
|
||||
WHERE id = $1::uuid
|
||||
RETURNING
|
||||
calendar_items.id::text,
|
||||
calendar_items.client_id::text,
|
||||
(SELECT name FROM clients WHERE clients.id = calendar_items.client_id),
|
||||
calendar_items.title,
|
||||
calendar_items.description,
|
||||
calendar_items.content_type,
|
||||
calendar_items.status,
|
||||
calendar_items.owner_id::text,
|
||||
calendar_items.scheduled_date::text,
|
||||
calendar_items.scheduled_at,
|
||||
calendar_items.copy_text,
|
||||
calendar_items.internal_notes,
|
||||
calendar_items.client_notes,
|
||||
calendar_items.created_by::text,
|
||||
calendar_items.created_at,
|
||||
calendar_items.updated_at
|
||||
`,
|
||||
input.ID,
|
||||
input.UpdateTitle,
|
||||
input.Title,
|
||||
input.UpdateDescription,
|
||||
input.Description,
|
||||
input.UpdateStatus,
|
||||
input.Status,
|
||||
input.UpdateScheduledDate,
|
||||
input.ScheduledDate,
|
||||
input.UpdateCopyText,
|
||||
input.CopyText,
|
||||
input.UpdateInternalNotes,
|
||||
input.InternalNotes,
|
||||
input.UpdateClientNotes,
|
||||
input.ClientNotes,
|
||||
)
|
||||
|
||||
return scanCalendarItem(row)
|
||||
}
|
||||
|
||||
func (s Store) CancelCalendarItem(ctx context.Context, id string) (CalendarItem, error) {
|
||||
row := s.db.QueryRow(ctx, `
|
||||
UPDATE calendar_items
|
||||
SET status = 'cancelado', updated_at = now()
|
||||
WHERE id = $1::uuid
|
||||
RETURNING
|
||||
calendar_items.id::text,
|
||||
calendar_items.client_id::text,
|
||||
(SELECT name FROM clients WHERE clients.id = calendar_items.client_id),
|
||||
calendar_items.title,
|
||||
calendar_items.description,
|
||||
calendar_items.content_type,
|
||||
calendar_items.status,
|
||||
calendar_items.owner_id::text,
|
||||
calendar_items.scheduled_date::text,
|
||||
calendar_items.scheduled_at,
|
||||
calendar_items.copy_text,
|
||||
calendar_items.internal_notes,
|
||||
calendar_items.client_notes,
|
||||
calendar_items.created_by::text,
|
||||
calendar_items.created_at,
|
||||
calendar_items.updated_at
|
||||
`, id)
|
||||
|
||||
return scanCalendarItem(row)
|
||||
}
|
||||
|
||||
func (s Store) DeleteCalendarItem(ctx context.Context, id string) error {
|
||||
command, err := s.db.Exec(ctx, `
|
||||
DELETE FROM calendar_items
|
||||
WHERE id = $1::uuid
|
||||
`, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if command.RowsAffected() == 0 {
|
||||
return ErrCalendarItemNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type calendarItemScanner interface {
|
||||
Scan(dest ...any) error
|
||||
}
|
||||
|
||||
func scanCalendarItem(row calendarItemScanner) (CalendarItem, error) {
|
||||
var item CalendarItem
|
||||
err := row.Scan(
|
||||
&item.ID,
|
||||
&item.ClientID,
|
||||
&item.ClientName,
|
||||
&item.Title,
|
||||
&item.Description,
|
||||
&item.ContentType,
|
||||
&item.Status,
|
||||
&item.OwnerID,
|
||||
&item.ScheduledDate,
|
||||
&item.ScheduledAt,
|
||||
&item.CopyText,
|
||||
&item.InternalNotes,
|
||||
&item.ClientNotes,
|
||||
&item.CreatedBy,
|
||||
&item.CreatedAt,
|
||||
&item.UpdatedAt,
|
||||
)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return CalendarItem{}, ErrCalendarItemNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return CalendarItem{}, err
|
||||
}
|
||||
return item, nil
|
||||
}
|
||||
Reference in New Issue
Block a user