### Install Teamwork App Code
Source: https://github.com/1312inc/webasyst-teamwork/blob/main/README.md
Clone the Teamwork app repository into the correct Webasyst directory. Ensure you are in the /PATH_TO_WEBASYST/wa-apps/ directory before executing the clone command.
```bash
cd /PATH_TO_WEBASYST/wa-apps/
mkdir tasks
git clone git://github.com/1312inc/Webasyst-Teamwork.git ./
```
--------------------------------
### Conditional Review Widget Display
Source: https://github.com/1312inc/webasyst-teamwork/blob/main/templates/actions/log/Log.html
Conditionally displays a review widget if the installer is available and the reviewWidget method exists. This is typically used for app reviews or setup guidance.
```html
{if $wa->installer && method_exists($wa->installer, 'reviewWidget')} {$wa->installer->reviewWidget('app/tasks')} {/if}
```
--------------------------------
### Register Teamwork App in Webasyst
Source: https://github.com/1312inc/webasyst-teamwork/blob/main/README.md
Add the 'tasks' entry to the `wa-config/apps.php` file to register the Teamwork app. This ensures Webasyst recognizes the app as installed.
```php
'tasks' => true,
```
--------------------------------
### Get Task AI Drawer
Source: https://github.com/1312inc/webasyst-teamwork/blob/main/templates/actions/tasks/TasksEdit.html
Fetches and initializes the AI drawer for task fields. It handles caching, AJAX requests, and displays an alert for timeouts.
```javascript
function getTaskAiDrawer(task_title, task_content) {
if (taskAiDrawer) {
return $.Deferred().resolve(taskAiDrawer).promise();
}
if (taskAiDrawerPromise) {
return taskAiDrawerPromise;
}
taskAiDrawerPromise = $.ajax({
url: '?module=tasks&action=ai&type=taskfields&html=1',
method: 'GET',
dataType: 'json',
timeout: 10000
})
.then(function (response) {
taskAiDrawer = $.waDrawer({
html: response.data?.html,
esc: false,
onBgClick: function () {
hideTaskAiDrawer();
},
onOpen: function ($wrapper) {
initTaskAiDrawer($wrapper, task_title, task_content);
}
});
return taskAiDrawer;
})
.fail(function (_jqXHR, textStatus) {
if (textStatus === 'timeout') {
$.waDialog.alert({ text: '[`Could not connect to Webasyst AI service. Please try again in a minute.`]' });
}
})
.always(function () {
taskAiDrawerPromise = null;
});
return taskAiDrawerPromise;
}
```
--------------------------------
### Backend Template Logic and Initialization
Source: https://github.com/1312inc/webasyst-teamwork/blob/main/templates/actions/backend/Backend.html
This snippet shows backend template logic for setting variables, iterating through priorities, and initializing the TasksController with account details, priorities, and user information. It also includes asset loading and header rendering.
```html
{$_app_name = $wa->appName(true)} {$_account_name = $wa->accountName(true)} {$_title = $wa->title()|escape} {$loc = explode('__', $wa->locale())} {$wa->title()|default:$_app_name} — {$_account_name} {strip}{$priorities = []} {foreach $wa->tasks->config('priorities') as $priority} {$priorities[$priority.value] = _w($priority.name)|escape} {/foreach}{/strip} window.TasksController.initBeforeJQuery({ accountName: {$wa->accountName(false)|json_encode}, priorities: {$priorities|json_encode}, app_icons: {$app_icons|json_encode}, contact_id: {$wa->user('id')}, is_admin: {if !empty($is_admin)}{$is_admin}{else}0{/if}, text_editor: {$text_editor|json_encode} }); window.static_url = '{$wa_app_static_url}'; {$wa->css()} {$wa->js()} {if $wa->locale() != 'en_US'} {/if} {"* @event backend_assets.%plugin_id% "} {foreach $backend_assets as $item} {$item} {/foreach}
{$wa->header()}
```
--------------------------------
### Initialize Task AI Drawer with Event Bindings
Source: https://github.com/1312inc/webasyst-teamwork/blob/main/templates/actions/tasks/TasksEdit.html
Initializes the task AI drawer, setting up initial values, and binding various event handlers for user interactions like trying free AI, pasting, copying, and hiding the drawer.
```javascript
function initTaskAiDrawer($wrapper, task_title, task_content) {
const AUTO_VALUE = '__auto__';
let try_free = !$wrapper.find('.js-premium-wrapper').length;
let lastGeneratedContent = '';
let lastGeneratedHtml = '';
bindTaskAiDrawerHideControls($wrapper);
if (!try_free) {
$wrapper.find('.js-premium-wrapper').show();
$wrapper.find('.dialog-ai-fields-container-wrapper').addClass('hidden');
$wrapper.find('.drawer-footer').addClass('hidden');
$wrapper.find('.drawer-header').addClass('hidden');
}
$wrapper.find('name="objective"').val(task_title + '\n' + task_content);
$wrapper.find('.js-try-free-ai').off('.taskAiDrawer').on('click.taskAiDrawer', function() {
const $self = $(this);
$.post('?module=tasks&action=ai&type=tryFree', (r) => {
if (r.status === 'ok') {
if (r.data.is_max_count) {
$wrapper.find('.js-after-try-free-ai').show();
$self.remove();
}
$wrapper.find('.js-premium-wrapper').hide();
$wrapper.find('.dialog-ai-fields-container-wrapper').removeClass('hidden');
$wrapper.find('.drawer-footer').removeClass('hidden');
$wrapper.find('.drawer-header').removeClass('hidden');
try_free = true;
}
});
});
$wrapper.find('.js-paste-ai-answer-result').off('.taskAiDrawer').on('click.taskAiDrawer', function(event) {
event.preventDefault();
if (!lastGeneratedContent) {
return;
}
window.lexicalEditor.updateContent('redactor-task-{$task_uuid}', lastGeneratedContent, { isMarkdown: true });
hideTaskAiDrawer();
});
$wrapper.find('.js-copy-ai-answer-result').off('.taskAiDrawer').on('click.taskAiDrawer', function(event) {
event.preventDefault();
if (!lastGeneratedContent) {
return;
}
copyTextToClipboard(lastGeneratedContent, lastGeneratedHtml)
.then(function () {
showTaskAiCopyNotification({ className: 'success', content: '[`Copied`]' });
})
.catch(function () {
showTaskAiCopyNotification({ className: 'danger', content: '[`Copying error`]' });
});
});
$wrapper.find('.js-write-ai-action').off('.taskAiDrawer').on('click.taskAiDrawer', function () {
if (!try_free) {
return false;
}
const $error_block =
```
--------------------------------
### Gantt Chart Initialization (JavaScript)
Source: https://github.com/1312inc/webasyst-teamwork/blob/main/templates/actions/milestones/MilestonesDefault.html
Initializes a GanttChart instance using provided milestone data and configuration options. Requires the GanttChart library to be loaded.
```javascript
(() => { const milestones = {json_encode(array_values($milestones))}; new GanttChart({ data: milestones, leftColId: 'left-col', timelineId: 'timeline', rightWrapperId: 'right-wrapper', timelineHeaderId: 'timeline-header', zoomSliderId: 'zoom-slider', locale: '{$wa->locale()}' }); })();
```
--------------------------------
### Get Spellcheck Source HTML
Source: https://github.com/1312inc/webasyst-teamwork/blob/main/templates/actions/tasks/TasksEdit.html
Retrieves the HTML content from the lexical editor input or the escaped value of the main element. Used as the source for spellchecking.
```javascript
function getSpellcheckSourceHtml () {
const $editorInput = $el.parent().find('.lexical-editor .editor-input').first();
const sourceHtml = $editorInput.length ? $editorInput.html().trim() : '';
if (sourceHtml) {
return sourceHtml;
}
const escapedHtml = escapeHtml($el.val()).replace(/\r?\n/g, '
');
return '