apiKey = config('trello.api_key'); $this->apiToken = config('trello.api_token'); $this->boardId = config('trello.board_id'); } /* ===================================================== | Public API ===================================================== */ /** * Create a new Trello card and populate base fields */ public function createCard( string $orderId, string $orderNumber, string $orderType = 'standard', ?string $startingListId = null ): ?string { if (! $this->isConfigured()) { Log::warning('Trello not configured, skipping card creation'); return null; } try { $listId = $startingListId ?? $this->getStartingListId($orderType); $response = Http::post("{$this->baseUrl}/cards", [ 'name' => "Order #{$orderNumber}", 'desc' => "Order ID: {$orderId}\nType: {$orderType}", 'idList' => $listId, 'key' => $this->apiKey, 'token' => $this->apiToken, ]); if (! $response->successful()) { Log::error('Failed to create Trello card', [ 'response' => $response->body(), ]); return null; } $cardId = $response->json('id'); if ($cardId) { $this->populateInitialFields($cardId, $orderId); } return $cardId; } catch (\Throwable $e) { Log::error('Exception creating Trello card', [ 'error' => $e->getMessage(), ]); return null; } } /** * Move card to another list */ public function moveCard(string $cardId, string $listName): bool { if (! $this->isConfigured()) { return false; } $listId = $this->getListIdByName($listName); if (! $listId) { Log::warning('Trello list not found', ['list' => $listName]); return false; } $response = Http::put("{$this->baseUrl}/cards/{$cardId}", [ 'idList' => $listId, 'key' => $this->apiKey, 'token' => $this->apiToken, ]); return $response->successful(); } /** * Attach a URL/file to a card */ public function attachFile(string $cardId, string $name, string $url): bool { $response = Http::post("{$this->baseUrl}/cards/{$cardId}/attachments", [ 'name' => $name, 'url' => $url, 'key' => $this->apiKey, 'token' => $this->apiToken, ]); return $response->successful(); } /** * Add a comment to a card */ public function addComment(string $cardId, string $text): bool { $response = Http::post("{$this->baseUrl}/cards/{$cardId}/actions/comments", [ 'text' => $text, 'key' => $this->apiKey, 'token' => $this->apiToken, ]); return $response->successful(); } /** * Mark checklist item complete */ public function checkItem(string $cardId, string $checklistName, string $itemName): bool { $card = Http::get("{$this->baseUrl}/cards/{$cardId}", [ 'checklists' => 'open', 'key' => $this->apiKey, 'token' => $this->apiToken, ])->json(); $checklist = collect($card['checklists'] ?? []) ->firstWhere('name', $checklistName); if (! $checklist) { return false; } $item = collect($checklist['checkItems'] ?? []) ->firstWhere('name', $itemName); if (! $item) { return false; } $response = Http::put( "{$this->baseUrl}/checklists/{$checklist['id']}/checkItems/{$item['id']}", [ 'state' => 'complete', 'key' => $this->apiKey, 'token' => $this->apiToken, ] ); return $response->successful(); } /* ===================================================== | Custom Fields ===================================================== */ public function setTextField(string $cardId, string $fieldKey, string $value): bool { return $this->setCustomField($cardId, $fieldKey, ['text' => $value]); } public function setNumberField(string $cardId, string $fieldKey, float|int $value): bool { return $this->setCustomField($cardId, $fieldKey, ['number' => (string) $value]); } public function setDateField(string $cardId, string $fieldKey, DateTimeInterface $date): bool { return $this->setCustomField($cardId, $fieldKey, [ 'date' => $date->format(DATE_ATOM), ]); } protected function setCustomField(string $cardId, string $fieldKey, array $value): bool { $fieldId = config("trello.custom_fields.{$fieldKey}"); if (! $fieldId) { Log::warning('Trello custom field not configured', ['field' => $fieldKey]); return false; } $response = Http::put( "{$this->baseUrl}/cards/{$cardId}/customField/{$fieldId}/item", [ 'value' => $value, 'key' => $this->apiKey, 'token' => $this->apiToken, ] ); return $response->successful(); } /* ===================================================== | Internals ===================================================== */ protected function populateInitialFields(string $cardId, string $orderId): void { $this->setTextField($cardId, 'order_id', $orderId); $this->setDateField($cardId, 'date_created', now()); } protected function getStartingListId(string $orderType): string { return $orderType === 'custom' ? config('trello.lists.custom.new_custom_order') : config('trello.lists.standard.new_order'); } protected function getListIdByName(string $listName): ?string { $lists = array_merge( config('trello.lists.standard', []), config('trello.lists.custom', []) ); foreach ($lists as $key => $id) { if (strtolower(str_replace('_', ' ', $key)) === strtolower($listName)) { return $id; } } return null; } protected function isConfigured(): bool { return ! empty($this->apiKey) && ! empty($this->apiToken) && ! empty($this->boardId); } }