| 1 |
<?php |
| 2 |
|
| 3 |
namespace FluentBoards\App\Hooks\Handlers; |
| 4 |
|
| 5 |
use FluentBoards\App\Models\Board; |
| 6 |
use FluentBoards\App\Models\Meta; |
| 7 |
use FluentBoards\App\Models\User; |
| 8 |
use FluentBoards\App\Models\Task; |
| 9 |
use FluentBoards\App\Services\Constant; |
| 10 |
|
| 11 |
class FluentCrmIntegration |
| 12 |
{ |
| 13 |
private const LEGACY_BOARD_ASSOCIATED_CRM_CONTACT = 'crm_contact'; |
| 14 |
|
| 15 |
public function registerCustomSection() |
| 16 |
{ |
| 17 |
$key = 'fluent_boards_in_fluent_crm'; |
| 18 |
$sectionTitle = __('FluentBoards', 'fluent-boards'); |
| 19 |
FluentCrmApi('extender')->addProfileSection($key, $sectionTitle, function ($contentArr, $subscriber) { |
| 20 |
$contentArr['heading'] = __('Fluent Boards', 'fluent-boards'); |
| 21 |
$contentArr['content_html'] = $this->prepareHtml($subscriber); |
| 22 |
|
| 23 |
return $contentArr; |
| 24 |
}); |
| 25 |
} |
| 26 |
|
| 27 |
public function prepareHtml($subscriber) |
| 28 |
{ |
| 29 |
$temp = Meta::query()->where('value', $subscriber->id) |
| 30 |
->whereIn('key', [ |
| 31 |
Constant::BOARD_ASSOCIATED_CRM_CONTACT, |
| 32 |
self::LEGACY_BOARD_ASSOCIATED_CRM_CONTACT, |
| 33 |
]) |
| 34 |
->where('object_type', Constant::OBJECT_TYPE_BOARD) |
| 35 |
->pluck('object_id'); |
| 36 |
|
| 37 |
$boards = Board::query() |
| 38 |
->whereIn('id', array_values(array_unique(array_map('intval', $temp->toArray())))) |
| 39 |
->whereNull('archived_at') |
| 40 |
->get(); |
| 41 |
|
| 42 |
$taskGroups = Task::where('crm_contact_id', $subscriber->id)->with('board')->get()->groupBy((function ($data) { |
| 43 |
return $data->board->title; |
| 44 |
})); |
| 45 |
|
| 46 |
|
| 47 |
|
| 48 |
$html = ''; |
| 49 |
$html .= '<style> |
| 50 |
.fbs_associated_boards_wrap { |
| 51 |
background: #FFFFFF; |
| 52 |
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); |
| 53 |
border-radius: 10px; |
| 54 |
overflow: hidden; |
| 55 |
margin-bottom: 30px; |
| 56 |
} |
| 57 |
.fbs_associated_boards_wrap .fbs_associated_board_header { |
| 58 |
display: flex; |
| 59 |
gap: 10px; |
| 60 |
align-items: center; |
| 61 |
justify-content: space-between; |
| 62 |
padding: 14px 16px; |
| 63 |
} |
| 64 |
.fbs_associated_boards_wrap .fbs_associated_board_header h3 { |
| 65 |
font-weight: 500; |
| 66 |
font-size: 16px; |
| 67 |
line-height: 140%; |
| 68 |
color: #1F2022; |
| 69 |
margin: 0; |
| 70 |
} |
| 71 |
.fbs_associated_boards_wrap .fbs_associated_board_header .fbs-go-to-board { |
| 72 |
display: block; |
| 73 |
background: #409eff; |
| 74 |
color: #ffffff; |
| 75 |
border-radius: 4px; |
| 76 |
padding: 4px 10px; |
| 77 |
font-size: 12px; |
| 78 |
} |
| 79 |
.fbs_associated_board_no_board{ |
| 80 |
background: #F6F7FA; |
| 81 |
padding: 5px 15px; |
| 82 |
} |
| 83 |
.fbs_associated_board_all_boards{ |
| 84 |
padding: 15px 10px; |
| 85 |
border-top: 2px solid #F6F7FA; |
| 86 |
} |
| 87 |
.fbs_associated_single-board{ |
| 88 |
height: auto; |
| 89 |
padding: 5px; |
| 90 |
line-height: 24px; |
| 91 |
font-size: 11px; |
| 92 |
white-space: normal; |
| 93 |
word-break: break-all; |
| 94 |
background: #e6ebf0; |
| 95 |
margin-right: 10px; |
| 96 |
color: #3c434a; |
| 97 |
} |
| 98 |
.fbs_associated_boards_wrap table { |
| 99 |
width: 100%; |
| 100 |
border-collapse: collapse; |
| 101 |
background: white; |
| 102 |
} |
| 103 |
.fbs_associated_boards_wrap table thead tr { |
| 104 |
border: none; |
| 105 |
} |
| 106 |
.fbs_associated_boards_wrap table thead tr th { |
| 107 |
border: none; |
| 108 |
background: #F6F7FA; |
| 109 |
font-weight: 600; |
| 110 |
font-size: 12px; |
| 111 |
line-height: 120%; |
| 112 |
color: #60646B; |
| 113 |
padding: 13px 16px; |
| 114 |
} |
| 115 |
.fbs_associated_boards_wrap table thead tr th:first-child { |
| 116 |
text-align: left; |
| 117 |
} |
| 118 |
.fbs_associated_boards_wrap table tbody tr td { |
| 119 |
text-align: center; |
| 120 |
font-weight: 400; |
| 121 |
font-size: 14px; |
| 122 |
line-height: 120%; |
| 123 |
color: #60646B; |
| 124 |
padding: 13px 16px; |
| 125 |
border-bottom: 1px solid #E5E9EF; |
| 126 |
} |
| 127 |
.fbs_associated_boards_wrap table tbody tr td a { |
| 128 |
color: #60646B; |
| 129 |
display: block; |
| 130 |
} |
| 131 |
.fbs_associated_boards_wrap table tbody tr td a:hover { |
| 132 |
color: #3C90FF; |
| 133 |
} |
| 134 |
.fbs_associated_boards_wrap table tbody tr td:first-child { |
| 135 |
text-align: left; |
| 136 |
} |
| 137 |
</style>'; |
| 138 |
|
| 139 |
$html .= '<div class="fbs_associated_boards_wrap">'; |
| 140 |
$html .= '<div class="fbs_associated_board_header"> |
| 141 |
<h3>All Boards</h3> |
| 142 |
</div>'; |
| 143 |
if (!$boards || $boards->isEmpty()) { |
| 144 |
$html .= '<div class="fbs_associated_board_no_board"> |
| 145 |
<p>No Board is associated with this contact</p> |
| 146 |
</div>'; |
| 147 |
}else{ |
| 148 |
$html .= '<div class="fbs_associated_board_all_boards">'; |
| 149 |
foreach ($boards as $board) { |
| 150 |
$html .= '<a href="'. fluent_boards_page_url() .'boards/'. $board->id .'" class="fbs_associated_single-board">'. $board->title .'</a>'; |
| 151 |
} |
| 152 |
$html .= '</div>'; |
| 153 |
} |
| 154 |
|
| 155 |
$html .= '</div>'; |
| 156 |
|
| 157 |
if (!$taskGroups || $taskGroups->isEmpty()) { |
| 158 |
$html .= '<div> |
| 159 |
<p>No associated Tasks with this contact</p> |
| 160 |
</div> |
| 161 |
'; |
| 162 |
} |
| 163 |
|
| 164 |
foreach ($taskGroups as $board) { |
| 165 |
$html .= '<div class="fbs_associated_boards_wrap">'; |
| 166 |
$html .= '<div class="fbs_associated_board_header"> |
| 167 |
<h3>' . $board[0]->board->title . '</h3> |
| 168 |
<a href="' . fluent_boards_page_url() . 'boards/' . $board[0]->board->id . '" class="fbs-go-to-board"> |
| 169 |
Go To Board |
| 170 |
</a> |
| 171 |
</div>'; |
| 172 |
$html .= '<table>'; |
| 173 |
$html .= '<thead> |
| 174 |
<tr> |
| 175 |
<th style="width: 250px;">' . __('Task Title', 'fluent-boards') . '</th> |
| 176 |
<th style="width: 130px;">' . __('Stage', 'fluent-boards') . '</th> |
| 177 |
<th style="width: 120px;">' . __('Start Date', 'fluent-boards') . '</th> |
| 178 |
<th style="width: 130px;">' . __('Due Date', 'fluent-boards') . '</th> |
| 179 |
<th style="width: 100px;">' . __('Priority', 'fluent-boards') . '</th> |
| 180 |
</tr> |
| 181 |
</thead>'; |
| 182 |
$html .= '<tbody>'; |
| 183 |
foreach ($board as $task) { |
| 184 |
$start = $task->start_at ? gmdate('jS F Y h:i A', strtotime($task->start_at)) : 'n/a'; |
| 185 |
$due = $task->due_date ? gmdate('jS F Y h:i A', strtotime($task->due_date)) : 'n/a'; |
| 186 |
$task->url = fluent_boards_page_url() . 'boards/' . $task->board_id . '/tasks/' . $task->id . '-' . substr(sanitize_title($task->title), 0, 40); |
| 187 |
$html .= '<tr>'; |
| 188 |
$html .= "<td><a href=\"{$task->url}\">{$this->substring($task->title, 40)}</a></td>"; |
| 189 |
$html .= "<td>{$task->stage->title}</td>"; |
| 190 |
$html .= '<td>' . $start . '</td>'; |
| 191 |
$html .= '<td>' . $due . '</td>'; |
| 192 |
$html .= "<td>{$task->priority}</td>"; |
| 193 |
|
| 194 |
$html .= '</tr>'; |
| 195 |
} |
| 196 |
$html .= '</tbody>'; |
| 197 |
$html .= '</table>'; |
| 198 |
$html .= '</div>'; |
| 199 |
} |
| 200 |
|
| 201 |
return $html; |
| 202 |
} |
| 203 |
|
| 204 |
public function substring($string, $length) |
| 205 |
{ |
| 206 |
if (strlen($string) > $length) { |
| 207 |
return substr($string, 0, $length) . '...'; |
| 208 |
} |
| 209 |
|
| 210 |
return $string; |
| 211 |
} |
| 212 |
} |
| 213 |
|