diff --git a/client/components/tickets/ticket-create.tsx b/client/components/tickets/ticket-create.tsx index 340914d..1fe4890 100644 --- a/client/components/tickets/ticket-create.tsx +++ b/client/components/tickets/ticket-create.tsx @@ -17,7 +17,7 @@ export interface TicketCreateProps { export default function TicketCreate({ onCancel, onCreate }: TicketCreateProps) { const { addToast } = useToast(); const { mutate: createTicket } = useCreateTicket(); - const [formData, setFormData] = useState>({ + const [formData, setFormData] = useState, "deadline"> & { deadline?: string }>({ status: "Open", title: "", description: "", @@ -26,6 +26,7 @@ export default function TicketCreate({ onCancel, onCreate }: TicketCreateProps) createdBy: "techsquad@digitalnest.org", site: "Watsonville", category: "Hardware", + deadline: "", }); const [isSaving, setIsSaving] = useState(false); @@ -54,17 +55,25 @@ export default function TicketCreate({ onCancel, onCreate }: TicketCreateProps) setIsSaving(true); try { - createTicket(formData, { - onSuccess: (ticket) => { - onCreate(ticket); - addToast("New ticket created successfully!", "Success", 3500); - }, - onError: (err) => { - console.error("Error creating ticket:", err); - addToast("An unexpected error occurred. Please try again.", "Error", 5000); - }, - onSettled: () => setIsSaving(false), - }); + // Prepare the ticket data, converting deadline string to Date and excluding empty deadline + const { deadline, ...rest } = formData; + const ticketData: Partial = { ...rest } as Partial; + + if (deadline && deadline !== "") { + ticketData.deadline = new Date(deadline); + } + + createTicket(ticketData, { + onSuccess: (ticket) => { + onCreate(ticket); + addToast("New ticket created successfully!", "Success", 3500); + }, + onError: (err) => { + console.error("Error creating ticket:", err); + addToast("An unexpected error occurred. Please try again.", "Error", 5000); + }, + onSettled: () => setIsSaving(false), + }); } catch (error) { console.error("Unexpected error:", error); setIsSaving(false); @@ -168,6 +177,17 @@ export default function TicketCreate({ onCancel, onCreate }: TicketCreateProps) + {/* Deadline input with date and time */} +
+ + setFormData((prev) => ({ ...prev, deadline: e.target.value }))} + /> +
+ {/* Deadline input with date and time */} +
+ + setFormData((prev) => ({ ...(prev as any), deadline: e.target.value }))} + className="w-full p-2 border border-gray-300 dark:border-gray-600 text-gray-800 dark:text-gray-300 rounded-md" + /> +
diff --git a/client/lib/types/ticket.ts b/client/lib/types/ticket.ts index af6456e..8d1c7ee 100644 --- a/client/lib/types/ticket.ts +++ b/client/lib/types/ticket.ts @@ -10,6 +10,7 @@ export default interface Ticket { status: Status; createdOn: Date; updatedAt: Date; + deadline?: Date; } export const Sites = ["Salinas", "Watsonville", "HQ", "Gilroy", "Modesto", "Stockton"] as const; diff --git a/server/internal/models/ticket.go b/server/internal/models/ticket.go index 0b5a018..f42a8c4 100644 --- a/server/internal/models/ticket.go +++ b/server/internal/models/ticket.go @@ -9,17 +9,18 @@ import ( // Ticket represents an IT ticket with associated metadata type Ticket struct { - ID string `json:"id" bson:"_id"` - Title string `json:"title"` - Description string `json:"description"` - Site string `json:"site"` - Category string `json:"category"` - AssignedTo string `json:"assignedTo"` - CreatedBy string `json:"createdBy"` - Priority int `json:"priority"` - Status string `json:"status"` - CreatedOn time.Time `json:"createdOn"` - UpdatedAt time.Time `json:"updatedAt"` + ID string `json:"id" bson:"_id"` + Title string `json:"title"` + Description string `json:"description"` + Site string `json:"site"` + Category string `json:"category"` + AssignedTo string `json:"assignedTo"` + CreatedBy string `json:"createdBy"` + Priority int `json:"priority"` + Status string `json:"status"` + CreatedOn time.Time `json:"createdOn"` + UpdatedAt time.Time `json:"updatedAt"` + Deadline *time.Time `json:"deadline,omitempty"` } // UnmarshalBSON provides a custom unmarshal implementation for Ticket, enabling @@ -28,17 +29,18 @@ func (t *Ticket) UnmarshalBSON(data []byte) error { var ( buffer bytes.Buffer result struct { - ID string `json:"id" bson:"_id"` - Title string `json:"title"` - Description string `json:"description"` - Site string `json:"site"` - Category string `json:"category"` - AssignedTo string `json:"assignedTo"` - CreatedBy string `json:"createdBy"` - Priority int `json:"priority"` - Status string `json:"status"` - CreatedOn time.Time `json:"createdOn"` - UpdatedAt time.Time `json:"updatedAt"` + ID string `json:"id" bson:"_id"` + Title string `json:"title"` + Description string `json:"description"` + Site string `json:"site"` + Category string `json:"category"` + AssignedTo string `json:"assignedTo"` + CreatedBy string `json:"createdBy"` + Priority int `json:"priority"` + Status string `json:"status"` + CreatedOn time.Time `json:"createdOn"` + UpdatedAt time.Time `json:"updatedAt"` + Deadline *time.Time `json:"deadline,omitempty"` } ) _, _ = buffer.Write(data) @@ -62,6 +64,7 @@ func (t *Ticket) UnmarshalBSON(data []byte) error { Status: result.Status, CreatedOn: result.CreatedOn, UpdatedAt: result.UpdatedAt, + Deadline: result.Deadline, } return nil diff --git a/server/internal/storage/storage.go b/server/internal/storage/storage.go index 42bd96e..2213694 100644 --- a/server/internal/storage/storage.go +++ b/server/internal/storage/storage.go @@ -65,6 +65,11 @@ func (s *TicketStore) CreateTicket(ctx context.Context, ticket models.Ticket) (i } ) + // Add deadline if it's provided + if ticket.Deadline != nil { + doc = append(doc, bson.E{Key: "deadline", Value: *ticket.Deadline}) + } + res, err := s.collection.InsertOne(ctx, doc) if err != nil { return "", err @@ -168,6 +173,25 @@ func (s *TicketStore) UpdateTicket(ctx context.Context, id string, updates map[s updatesDoc = append(updatesDoc, bson.E{Key: "status", Value: status}) } + if deadline, ok := updates["deadline"].(string); ok { + var parsedDeadline time.Time + var err error + + // Try datetime-local format first (YYYY-MM-DDTHH:MM) + if parsedDeadline, err = time.Parse("2006-01-02T15:04", deadline); err != nil { + // Try date-only format (YYYY-MM-DD) + if parsedDeadline, err = time.Parse("2006-01-02", deadline); err != nil { + // Try RFC3339 format as fallback + if parsedDeadline, err = time.Parse(time.RFC3339, deadline); err != nil { + sugar.Debugw("failed to parse deadline", "error", err, "value", deadline) + return nil, err + } + } + } + + updatesDoc = append(updatesDoc, bson.E{Key: "deadline", Value: parsedDeadline}) + } + if len(updates) > 0 { updatesDoc = append(updatesDoc, bson.E{Key: "updatedAt", Value: time.Now()}) }