* remove biz feature files from the open source version.

This commit is contained in:
jyeu
2023-08-18 10:43:07 +08:00
parent 0891d2310d
commit 2bdb968309
12 changed files with 0 additions and 1674 deletions
-4
View File
@@ -1,4 +0,0 @@
.content-row {display: flex; flex-direction: row; padding: 8px 0px;}
.input-label {width: 120px; flex-shrink: 0; padding: 6px 12px; text-align: right;}
.input {flex-grow: 1;}
.main-header {border-bottom-color: #E6EAF1;}
-11
View File
@@ -1,11 +0,0 @@
(function()
{
const inputs = [...Array.from(document.querySelectorAll('input')), ...Array.from(document.querySelectorAll('textarea'))];
inputs.forEach(element => {
const displayElement = document.getElementById(element.name + 'Display');
if(!displayElement) return;
element.addEventListener('input', () => {
displayElement.innerText = element.value;
});
});
})();
-60
View File
@@ -1,60 +0,0 @@
<?php
/**
* The ai createPrompt view file of ai module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Wenrui LI <liwenrui@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.lite.html.php';?>
<?php js::set('requiredFields', $config->ai->createprompt->requiredFields);?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $this->lang->ai->prompts->create;?></h2>
</div>
<form method="post" class="main-form form-ajax" target="hiddenwin">
<table class="table table-form">
<tbody>
<tr>
<th class="w-80px"><?php echo $this->lang->ai->prompts->name;?></th>
<td><?php echo html::input('name', '', "class='form-control'");?></td>
</tr>
<tr>
<th class="w-80px"><?php echo $this->lang->ai->prompts->description;?></th>
<td><?php echo html::textarea('desc', '', "rows='6' class='form-control'");?></td>
</tr>
<tr>
<td colspan="2" class="text-center form-actions">
<?php echo html::submitButton($this->lang->ai->nextStep, 'disabled', 'btn btn-wide btn-primary disabled');?>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
<script>
$(function() {
$('input[name="name"]').on('input', function()
{
if($(this).val().length > 0)
{
$('button[type="submit"]').removeClass('disabled').removeAttr('disabled');
}
else
{
$('button[type="submit"]').addClass('disabled').attr('disabled', 'disabled');
}
});
});
function gotoPrompt(id)
{
parent.location.href = <?php echo commonModel::hasPriv('ai', 'promptassignrole') ? "createLink('ai', 'promptassignrole', 'prompt='" : "createLink('ai', 'promptview', 'id='";?> + id);
}
</script>
<?php include '../../common/view/footer.lite.html.php';?>
-283
View File
@@ -1,283 +0,0 @@
<?php
/**
* The ai prompt role assign view file of ai module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Wenrui LI <liwenrui@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<style>
.center-wrapper {display: flex; justify-content: center; height: 100%;}
.center-content {min-width: 800px; height: 100%; display: flex; flex-direction: column;}
.content-row {display: flex; flex-direction: row; padding: 8px 0px;}
.input-label {width: 120px; padding: 6px 12px; text-align: right;}
.input {flex-grow: 1;}
.v-top > * {vertical-align: top; display: inline-block;}
.role-template-card p {margin: 0;}
#roleTemplate {overflow: hidden;}
#roleTemplate .btn-link:focus {background: unset;}
#roleListContainer {height: 100%; overflow-y: scroll;}
</style>
<?php include 'promptdesignprogressbar.html.php';?>
<div id='mainContent' class='main-content' style='height: calc(100vh - 120px); padding: 0;'>
<form id="mainForm" onsubmit="return validateForm();" class='load-indicator main-form form-ajax' method='post' style='height: 100%;'>
<div class='center-wrapper'>
<div class='center-content' style="width: 100%; display: flex; flex-direction: row;">
<div style="flex-grow: 1; padding: 20px;">
<div>
<div style="display: flex; justify-content: space-between; align-items: center;">
<h4><?php echo $lang->ai->prompts->assignRole;?></h4>
<?php echo html::commonButton("<span>{$lang->ai->prompts->roleTemplate}</span> " . "<icon class='icon icon-first-page'></icon> ", 'id="expandRoleTemplatePanel"', 'btn btn-info');?>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->model;?></span></div>
<div class='input mw-400px'>
<?php echo html::select('model', $lang->ai->models->typeList, current(array_keys($lang->ai->models->typeList)), "class='form-control chosen' required");?>
</div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->role;?></span></div>
<div class='input mw-400px'><?php echo html::input('role', $prompt->role, "class='form-control' placeholder='{$lang->ai->prompts->rolePlaceholder}'");?></div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->characterization;?></span></div>
<div class='input'><?php echo html::textarea('characterization', $prompt->characterization, "class='form-control' rows='4' placeholder='{$lang->ai->prompts->charPlaceholder}'");?></div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->roleTemplateSave;?></span></div>
<div class='input' style="display: flex; align-items: center;"><?php echo html::radio('saveTemplate', $lang->ai->prompts->roleTemplateSaveList, 'discard');?></div>
</div>
</div>
<div style='display: flex; flex-grow: 1; flex-direction: column-reverse;'>
<div style='display: flex; justify-content: center;'><?php echo html::submitButton($lang->ai->nextStep, 'disabled name="jumpToNext" value="1"');?></div>
</div>
</div>
<div id="roleTemplate" style="display: none; width: 370px; flex-grow: 0; padding: 20px 24px; border-left: 1px solid #E6EAF1; background-color: #FCFDFE; border-top-right-radius: 4px; border-bottom-right-radius: 4px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
<h4 class="v-top"">
<?php echo "<span style='margin-right: 4px;'>{$lang->ai->prompts->roleTemplate}</span>" . " <i class='icon icon-help' data-toggle='tooltip' data-placement='top' title='{$lang->ai->prompts->roleTemplateTip}'></i>";?>
</h4>
<?php echo html::commonButton("<i class='icon icon-plus icon-sm text-primary'></i>", 'data-toggle="modal" data-target="#createRoleTemplateModal"', 'btn btn-link'); ?>
</div>
<div id="roleListContainer">
<?php include './roletemplates.html.php';?>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="modal fade" id="createRoleTemplateModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only"><?php echo $lang->close; ?></span></button>
<h4 class="modal-title"><?php echo $lang->ai->prompts->addRoleTemplate; ?></h4>
</div>
<div class="modal-body">
<form id="createRoleForm" class="not-watch">
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->role;?></span></div>
<div class='input mw-400px'><?php echo html::input('role', '', "class='form-control' placeholder='{$lang->ai->prompts->rolePlaceholder}'");?></div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->characterization;?></span></div>
<div class='input'><?php echo html::textarea('characterization', '', "class='form-control' rows='4' placeholder='{$lang->ai->prompts->charPlaceholder}'");?></div>
</div>
</form>
</div>
<div class="modal-footer">
<button id="createRoleButton" type="button" class="btn btn-primary" data-dismiss="modal"><?php echo $lang->save; ?></button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="editRoleTemplateModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only"><?php echo $lang->close; ?></span></button>
<h4 class="modal-title"><?php echo $lang->ai->prompts->editRoleTemplate; ?> <i class='icon icon-help'></i> <span class="text-gray"><?php echo $lang->ai->prompts->editRoleTemplateTip;?></span></h4>
</div>
<div class="modal-body">
<form id="editRoleForm" class="not-watch">
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->role;?></span></div>
<div class='input mw-400px'><?php echo html::input('role', '', "class='form-control' placeholder='{$lang->ai->prompts->rolePlaceholder}'");?></div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->characterization;?></span></div>
<div class='input'><?php echo html::textarea('characterization','', "class='form-control' rows='4' placeholder='{$lang->ai->prompts->charPlaceholder}'");?></div>
</div>
<input type="hidden" name="id">
</form>
</div>
<div class="modal-footer">
<button id="editRoleButton" type="button" class="btn btn-primary" data-dismiss="modal"><?php echo $lang->save; ?></button>
</div>
</div>
</div>
</div>
<script>
function validateForm()
{
let pass = true;
const model = document.getElementById('model')?.value;
if(!model)
{
$.zui.messager.danger('<?php echo sprintf($lang->ai->validate->noEmpty, $lang->ai->prompts->model);?>');
pass = false;
}
return pass;
}
(function ()
{
let roleId = null;
$(function () {
$('[data-toggle="tooltip"]').tooltip();
$('select[name="model"]').change(function()
{
var model = $(this).val();
if (model) $('button[type="submit"]').removeAttr('disabled');
if (!model) $('button[type="submit"]').attr('disabled', 'disabled');
});
$('select[name="model"]').trigger('change');
});
$('#createRoleButton').on('click', function(e)
{
e.preventDefault();
const formData = new FormData(document.getElementById('createRoleForm'));
if(!formData.get('role'))
{
$.zui.messager.danger('<?php echo sprintf($lang->ai->validate->noEmpty, $lang->ai->prompts->role);?>');
return false;
}
$.ajax({
url: createLink('ai', 'roleTemplates'),
type: 'POST',
data: {method: 'create', role: formData.get('role'), characterization: formData.get('characterization')},
dataType: 'html',
success: function(response)
{
$('#createRoleForm [name="role"], #createRoleForm [name="characterization"]').each(function() {$(this).val('');});
$.zui.messager.success('<?php echo $lang->ai->prompts->roleAddedSuccess; ?>');
$('#roleList').html($($.parseHTML(response)).filter('#roleList').html());
}
});
});
$('#editRoleButton').on('click', function(e)
{
e.preventDefault();
const formData = new FormData(document.getElementById('editRoleForm'));
if(!formData.get('role'))
{
$.zui.messager.danger('<?php echo sprintf($lang->ai->validate->noEmpty, $lang->ai->prompts->role);?>');
return false;
}
$.ajax({
url: createLink('ai', 'roleTemplates'),
type: 'POST',
data: {
method: 'edit',
id: formData.get('id'),
role: formData.get('role'),
characterization: formData.get('characterization')
},
dataType: 'html',
success: function(response)
{
$('#editRoleForm [name="role"], #editRoleForm [name="characterization"]').each(function() {$(this).val('');});
$.zui.messager.success('<?php echo $lang->ai->prompts->roleAddedSuccess; ?>');
$('#roleList').html($($.parseHTML(response)).filter('#roleList').html());
}
});
});
$('#roleListContainer').on('click', function(e)
{
const button = e.target.closest('#roleListContainer button');
if(!button) return;
const card = button.closest('.role-template-card');
if(!card) return;
e.preventDefault();
const id = card.dataset.id;
switch (button.dataset.action)
{
case 'apply':
{
$('#mainForm input[name="role"]').val(card.querySelector('.role').innerText).toggleClass('focus', true);
$('#mainForm textarea[name="characterization"]').val(card.querySelector('.characterization').innerText).toggleClass('focus', true);
setTimeout(() => {
$('#mainForm input[name="role"]').toggleClass('focus', false);
$('#mainForm textarea[name="characterization"]').toggleClass('focus', false);
}, 1000);
roleId = id;
break;
}
case 'edit':
{
const model = $('#editRoleTemplateModal');
model.find('input[name="id"]').val(id);
model.find('input[name="role"]').val(card.querySelector('.role').innerText);
model.find('textarea[name="characterization"]').val(card.querySelector('.characterization').innerText);
model.modal('toggle');
break;
}
case 'del':
{
if (window.confirm('<?php echo $lang->ai->prompts->roleDelConfirm; ?>')) {
$.ajax({
url: createLink('ai', 'roleTemplates'),
type: 'POST',
data: {method: 'delete', id: id},
dataType: 'html',
success: function (response) {
$.zui.messager.success('<?php echo $lang->ai->prompts->roleDelSuccess; ?>');
$('#roleList').html($($.parseHTML(response)).filter('#roleList').html());
}
});
}
break;
}
}
});
$('#mainForm').on('submit', function()
{
if($('#mainForm input[name="saveTemplate"]:checked').val() === 'discard') return true;
if((roleId && !$(`#role-template-${roleId}`).length) || ($('#mainForm input[name="role"]').val() === $(`role-${roleId}`).text() && $('#mainForm textarea[name="characterization"]').val() === $(`characterization-${roleId}`).text()))
{
$('#mainForm input[name="saveTemplate"]#saveTemplatediscard').prop('checked', true);
}
return true;
});
const expandRoleTemplatePanel = document.getElementById('expandRoleTemplatePanel');
if(expandRoleTemplatePanel)
{
expandRoleTemplatePanel.addEventListener('click', function (e) {
const roleTemplate = document.getElementById('roleTemplate');
const isPanelExpanded = roleTemplate.style.display === 'block';
expandRoleTemplatePanel.querySelector('icon').className = isPanelExpanded ? 'icon icon-first-page' : 'icon icon-last-page';
if (roleTemplate) {
roleTemplate.style.display = isPanelExpanded ? 'none' : 'block';
}
});
}
})();
</script>
<?php include '../../common/view/footer.html.php';?>
-145
View File
@@ -1,145 +0,0 @@
<?php
/**
* The design step bar view file of AI module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Jianyu Chen <chenjianyu@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.lite.html.php'; ?>
<div id='mainContent' class='main-content'>
<form method="post" class="main-form form-ajax">
<div class='center-block'>
<div class="main-header">
<p>
<?php echo $this->lang->ai->audit->designPrompt; ?>
<strong><?php echo $prompt->name ?></strong>
<span class='label label-id'> <?php echo $prompt->id; ?></span>
</p>
</div>
<div class="bg-gray-3" style="display: flex;">
<div style="flex-basis: 50%; flex-grow: 1;padding: 5px 20px 5px 10px; border-right: #E6EAF1 1px solid">
<h4><?php echo $this->lang->ai->prompts->assignRole; ?></h4>
<div class='content-row'>
<div class='input-label'><span><?php echo $this->lang->ai->prompts->role; ?></span></div>
<div
class='input'><?php echo html::input('role', $prompt->role, "class='form-control' placeholder='{$this->lang->ai->prompts->rolePlaceholder}'"); ?></div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $this->lang->ai->prompts->characterization; ?></span></div>
<div
class='input'><?php echo html::textarea('characterization', $prompt->characterization, "class='form-control' rows='4' placeholder='{$this->lang->ai->prompts->charPlaceholder}'"); ?></div>
</div>
<h4><?php echo $this->lang->ai->prompts->selectDataSource; ?></h4>
<div class='content-row'>
<div class='input-label text-gray'><span><?php echo $this->lang->ai->prompts->object; ?></span></div>
<div class='input'
style="text-align: left; padding: 6px 0;"><?php echo $this->lang->ai->dataSource[$prompt->module]['common']; ?></div>
</div>
<div class='content-row'>
<div class='input-label text-gray'><span><?php echo $this->lang->ai->prompts->field; ?></span></div>
<div class='input' style="padding: 6px 0;">
<?php
$sources = explode(',', $prompt->source);
$sources = array_filter($sources);
end($sources);
$lastKey = key($sources);
foreach($sources as $key => $source)
{
$isLastElem = ($key === $lastKey);
list($object, $field) = explode('.', $source);
echo $this->lang->ai->dataSource[$prompt->module][$object][$field] . ($isLastElem ? '' : $this->lang->separater);
}
?>
</div>
</div>
<h4><?php echo $this->lang->ai->prompts->setPurpose; ?></h4>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->purpose; ?></span></div>
<div
class='input'><?php echo html::textarea('purpose', $prompt->purpose, "class='form-control' rows='6' placeholder='{$lang->ai->prompts->purposeTip}' required"); ?></div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->elaboration; ?></span></div>
<div
class='input'><?php echo html::textarea('elaboration', $prompt->elaboration, "class='form-control' rows='6' placeholder='{$lang->ai->prompts->elaborationTip}'"); ?></div>
</div>
<h4><?php echo $this->lang->ai->prompts->setTargetForm; ?></h4>
<div class='content-row'>
<div class='input-label'><span><?php echo $this->lang->ai->prompts->selectTargetForm; ?></span></div>
<div class='input' style="padding: 6px 0;">
<?php
$targetForm = explode('.', $prompt->targetForm);
echo $this->lang->ai->targetForm[$targetForm[0]][$targetForm[1]];
?>
</div>
</div>
</div>
<div style="flex-basis: 50%; flex-grow: 1; display: flex; flex-direction: column; word-break: break-all;">
<div style="padding: 5px 10px; border-bottom: #E6EAF1 1px solid">
<h4
style="margin-bottom: 24px;"><?php echo sprintf($this->lang->ai->models->promptFor, $this->lang->ai->models->typeList['openai-gpt35']); ?></h4>
<p class="text-gray"><?php echo $this->lang->ai->prompts->assignRole; ?></p>
<p id="roleDisplay"><?php echo $prompt->role; ?></p>
<p id="characterizationDisplay"><?php echo $prompt->characterization; ?></p>
</div>
<div style="padding: 5px 10px; border-bottom: #E6EAF1 1px solid">
<p class="text-gray"><?php echo $this->lang->ai->prompts->selectDataSource; ?></p>
<p><?php echo htmlspecialchars($dataPrompt); ?></p>
</div>
<div style="padding: 5px 10px;">
<p class="text-gray"><?php echo $this->lang->ai->prompts->setPurpose; ?></p>
<p id="purposeDisplay"><?php echo $prompt->purpose; ?></p>
<p id="elaborationDisplay"><?php echo $prompt->elaboration; ?></p>
</div>
</div>
</div>
<div style="margin-top: 15px; padding-left: 10px;">
<p style="display: inline-block"><?php echo $this->lang->ai->audit->afterSave; ?></p>
<?php echo html::radio('backLocation', $this->lang->ai->audit->backLocationList, 1); ?>
</div>
<div style="display: flex; justify-content: center; margin-top: 10px;">
<?php echo html::submitButton($this->lang->save, '', 'btn btn-primary'); ?>
</div>
</div>
</form>
</div>
<script>
(function()
{
const submitButton = document.getElementById('submit');
if(!submitButton) return;
const purpose = document.getElementsByName('purpose')[0];
if(!purpose) return;
if(!purpose.value)
{
submitButton.disabled = true;
}
purpose.addEventListener('input', function()
{
submitButton.disabled = !purpose.value;
});
})();
function reloadPrompt(promptId, objectId)
{
parent.$.zui.closeModal();
$('body', window.parent.document).attr('data-loading', '<?php echo $lang->ai->execute->loading;?>');
$('body', window.parent.document).addClass('load-indicator loading');
let link = createLink('ai', 'promptexecute', 'promptId=' + promptId + '&objectId=' + objectId);
link = link.replace('onlybody=yes', '');
const aTag = document.createElement('a');
aTag.href = link;
aTag.style.display = 'none';
parent.document.body.appendChild(aTag);
aTag.click();
}
</script>
<?php include '../../common/view/footer.lite.html.php'; ?>
@@ -1,159 +0,0 @@
<?php
/**
* The design step bar view file of AI module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Jianyu Chen <chenjianyu@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<header id="mainMenu" class='design-steps'>
<?php echo html::a(inlink("prompts"), html::commonButton("<i class='icon icon-angle-left'></i> $lang->goback", 'style="font-weight: bold;"', 'btn btn-link'));?>
<div class='steps'>
<?php
$step = preg_replace('/^prompt/', '', $app->methodName);
$stepSequence = array('assignrole', 'selectdatasource', 'setpurpose', 'settargetform', 'finalize');
$currentStepIndex = array_search($step, $stepSequence);
$lastActiveStepIndex = array_search($lastActiveStep, $stepSequence);
$stepstatus = array();
foreach($stepSequence as $index => $stepName)
{
if($index < $currentStepIndex)
{
$stepstatus[$stepName] = 'active';
}
elseif($index > $currentStepIndex && $index <= $lastActiveStepIndex + 1)
{
$stepstatus[$stepName] = 'clickable';
}
else
{
$stepstatus[$stepName] = 'disabled';
}
if($index == $currentStepIndex)
{
$stepstatus[$stepName] = 'current';
}
}
foreach($lang->ai->designStepNav as $stepKey => $stepLang)
{
$arrow = '';
$currentStepStatus = current($stepstatus);
$nextStepStatus = next($stepstatus);
if($stepKey != end($stepSequence))
{
$arrowClass = $currentStepStatus == $nextStepStatus ||($currentStepStatus == 'clickable' && $nextStepStatus == 'disabled') ? 'outline-arrow' : 'solid-arrow';
$arrow = "<div class='$arrowClass'></div>";
}
$aTag = html::a(inlink("prompt$stepKey", "prompt=$prompt->id"), $stepLang);
echo "<div class='step $currentStepStatus'>$aTag$arrow</div>";
}
?>
</div>
<button id="saveButton" type="submit" class="btn btn-primary visibility-hidden" form="mainForm"><i class="icon icon-save icon-sm"></i> <?php echo $lang->save ?></button>
<div class="modal fade" id="returnConfirmModal">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-body">
<p><?php echo sprintf($lang->ai->validate->dirtyForm, $lang->ai->designStepNav[$step])?></p>
</div>
<div class="modal-footer">
<div class="footer">
<div>
<?php echo html::a(inlink("prompts"), html::commonButton("<i class='icon icon-back icon-sm'></i> $lang->goback", '', 'btn btn-info'));?>
</div>
<div>
<button type="button" class="btn btn-default" data-dismiss="modal"><?php echo $lang->cancel?></button>
<button type="button" class="btn btn-primary"><?php echo $lang->save?></button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let isPromptDesignDirty = false;
(function()
{
const steps = document.getElementsByClassName('steps')[0];
if(steps)
{
for(let step of steps.children)
{
step.addEventListener('mouseenter', function()
{
if(step.lastElementChild?.classList.contains('outline-arrow'))
{
step.lastElementChild.classList.remove('outline-arrow');
step.lastElementChild.classList.add('hover-arrow');
}
if(step.previousElementSibling?.lastElementChild?.classList.contains('outline-arrow'))
{
step.previousElementSibling.lastElementChild.classList.add('solid-arrow');
}
});
step.addEventListener('mouseleave', function()
{
if(step.lastElementChild?.classList.contains('hover-arrow'))
{
step.lastElementChild.classList.remove('hover-arrow');
step.lastElementChild.classList.add('outline-arrow');
}
if(step.previousElementSibling?.lastElementChild?.classList.contains('outline-arrow') && step.previousElementSibling?.lastElementChild?.classList.contains('solid-arrow'))
{
step.previousElementSibling.lastElementChild.classList.remove('solid-arrow');
}
});
const link = step.querySelector('a');
if(!link) continue;
link.addEventListener('click', function(e)
{
if(!isPromptDesignDirty) return;
const result = validateForm();
if(!result) {
e.preventDefault();
return;
}
const submitButton = document.getElementById('saveButton');
if(!submitButton) return;
submitButton.click();
})
}
}
})();
(function()
{
const backButton = document.querySelector('.design-steps button:first-child');
if(!backButton) return;
backButton.addEventListener('click', function(e)
{
if(!isPromptDesignDirty) return;
$('#returnConfirmModal').modal('show', 'fit');
e.preventDefault();
});
const modalSaveButton = document.querySelector('#returnConfirmModal .btn-primary');
if(!modalSaveButton) return;
modalSaveButton.addEventListener('click', function()
{
if(validateForm() === false) return;
const submitButton = document.getElementById('saveButton');
if(!submitButton) return;
submitButton.click();
location.href = createLink('ai', 'prompts');
});
})();
</script>
</header>
-55
View File
@@ -1,55 +0,0 @@
<?php
/**
* The ai prompt edit view file of ai module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Wenrui LI <liwenrui@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php js::set('requiredFields', $config->ai->createprompt->requiredFields);?>
<div id='mainContent' class='main-content'>
<div class='center-block'>
<div class='main-header'>
<h2><?php echo $this->lang->ai->prompts->edit . " $lang->colon " . $prompt->name;?></h2>
</div>
<form method="post" class="main-form form-ajax" target="hiddenwin">
<table class="table table-form">
<tbody>
<tr>
<th class="w-80px"><?php echo $this->lang->ai->prompts->name;?></th>
<td><?php echo html::input('name', $prompt->name, "class='form-control'");?></td>
</tr>
<tr>
<th class="w-80px"><?php echo $this->lang->ai->prompts->description;?></th>
<td><?php echo html::textarea('desc', $prompt->desc, "rows='6' class='form-control'");?></td>
</tr>
<tr>
<td colspan="2" class="text-center form-actions">
<?php echo html::submitButton($lang->save, '', 'btn btn-wide btn-primary');?>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
<script>
$(function() {
$('input[name="name"]').on('input', function()
{
if($(this).val().length > 0)
{
$('button[type="submit"]').removeClass('disabled').removeAttr('disabled');
}
else
{
$('button[type="submit"]').addClass('disabled').attr('disabled', 'disabled');
}
});
});
</script>
<?php include '../../common/view/footer.html.php';?>
-80
View File
@@ -1,80 +0,0 @@
<?php
/**
* The ai prompt finalize view file of ai module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Wenrui LI <liwenrui@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<style>
.center-wrapper {display: flex; justify-content: center; height: 100%;}
.center-content {width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center;}
#info-form {display: flex; flex-direction: column; min-width: 800px;}
.content-row {display: flex; flex-direction: row; padding: 8px 0px;}
.input-label {width: 120px; padding: 6px 12px; text-align: right;}
.input {flex-grow: 1;}
.input-tip {padding: 6px 12px;}
</style>
<?php include 'promptdesignprogressbar.html.php';?>
<div id='mainContent' class='main-content' style='height: calc(100vh - 120px);'>
<form id="mainForm" onsubmit="return validateForm();" class='load-indicator main-form form-ajax' method='post' style='height: 100%;'>
<div class='center-wrapper'>
<div class='center-content'>
<div id='info-form'>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->prompt->name;?></span></div>
<div class='input mw-600px'><?php echo html::input('name', $prompt->name, "class='form-control' required");?></div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->prompt->module;?></span></div>
<div class='input mw-200px'><?php echo html::input('module', $lang->ai->prompts->modules[$prompt->module], "class='form-control' disabled");?></div>
<div class='input-tip text-gray'><?php echo $lang->ai->moduleDisableTip;?></div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->prompt->desc;?></span></div>
<div class='input mw-600px'><?php echo html::textarea('desc', $prompt->desc, "rows='6' class='form-control'");?></div>
</div>
</div>
<div style='display: flex; flex-grow: 1; flex-direction: column-reverse;'>
<div style='display: flex; justify-content: center;'><?php echo html::submitButton($lang->ai->prompts->action->publish, 'disabled name="jumpToNext" value="1"');?></div>
</div>
</div>
</div>
</form>
</div>
<script>
function validateForm()
{
let pass = true;
const name = document.getElementById('name')?.value;
if(!name)
{
$.zui.messager.danger('<?php echo sprintf($lang->ai->validate->noEmpty, $lang->ai->prompts->name);?>');
pass = false;
}
return pass;
}
$(function() {
$('input[name="name"]').on('input', function()
{
if($(this).val().length > 0)
{
$('#submit').removeAttr('disabled');
}
else
{
$('#submit').attr('disabled', 'disabled');
}
});
$('input[name="name"]').trigger('input');
});
</script>
<?php include '../../common/view/footer.html.php';?>
@@ -1,492 +0,0 @@
<?php
/**
* The ai prompt data source selection view file of ai module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Wenrui LI <liwenrui@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<?php js::set('dataSource', $dataSource);?>
<?php js::set('dataSourceLang', $lang->ai->dataSource);?>
<style>
.center-wrapper {display: flex; justify-content: center; height: 100%;}
.center-content {width: 100%; height: 100%; display: flex; flex-direction: column;}
#data-selector {display: flex; flex-direction: row; height: 100%; margin-bottom: 16px; max-height: calc(100% - 48px);}
#data-sources {flex-grow: 2; display: flex; flex-direction: row; margin-right: 16px;}
#data-selected {flex-grow: 1; display: flex; flex-direction: column;}
.heading > * {display: inline-block; padding: 4px 0;}
#data-category-select {flex-grow: 1; background-color: #f8f8f8; border: 1px solid #eee; border-right: none;}
#data-category-select > ul {padding: 16px; margin: 0; list-style: none;}
#data-category-select > ul > li > a.active {font-weight: bold;}
#data-categories {flex-grow: 1; display: flex; flex-direction: column;}
#data-properties {flex-grow: 3; display: flex; flex-direction: column; height: 100%;}
#data-properties > .heading {padding: 4px 20px;}
#data-properties > .heading > h4 {margin: 6px 0;}
#data-property-select {flex-grow: 1; border: 1px solid #eee; border-left: none; height: calc(100% - 44px); overflow-y: auto;}
#data-property-selector {height: 100%; width: 100%;}
#data-selected-items {flex-grow: 1; border: 1px solid #eee; height: calc(100% - 44px); overflow-y: auto; scrollbar-gutter: stable;}
.obj-view {display: flex; flex-direction: column; padding: 20px;}
.obj-view:not(:first-of-type) {border-top: 1px solid #eee;}
.obj-view-body {display: grid; padding: 4px 20px; grid-template-columns: repeat(4, 1fr);}
.obj-view-header.checkbox-inline, .obj-view-item.checkbox-inline {margin: 4px; cursor: unset;}
.obj-view-item.checkbox-inline+.obj-view-item.checkbox-inline {margin-left: 4px; margin-top: 4px;}
.checkbox-inline > label, .checkbox-inline > input {cursor: pointer;}
.checkbox-primary > label {padding-left: 0;}
#selected-data-sorter .list-group {padding: 0; margin: 0; list-style: none; overflow: hidden;}
#selected-data-sorter .list-group-item {padding: 8px 16px; display: flex; align-items: center; cursor: move; transition: background-color 0.2s ease-in-out;}
#selected-data-sorter .list-group-item > span {flex-grow: 1;}
#selected-data-sorter .list-group-item:hover {background-color: #eef5ff;}
#selected-data-sorter .list-group-item .drag-icon {padding-right: 8px; opacity: 0; transition: opacity 0.2s ease-in-out;}
#selected-data-sorter .list-group-item:hover .drag-icon {opacity: 1;}
#selected-data-sorter .list-group-item::before {content: counter(list-item); counter-increment: list-item; display: inline-block; width: 2ch; padding-right: 28px;}
#selected-data-sorter .list-group-item.drag-shadow::before {content: '';}
#selected-data-sorter .list-group-item .remove-icon {cursor: pointer; padding: 2px;}
@media (max-width: 1366px)
{
.obj-view-body {grid-template-columns: repeat(3, 1fr);}
}
</style>
<script>
/* Store and sync value of datasource input, change by checkbox changes. */
class DataSourceStore
{
value = [];
listeners = [];
/* Get store value. */
get value()
{
return this.value;
}
/* Set store value. */
set value(value)
{
this.value = value;
}
/* Reset store value. */
reset()
{
this.value = [];
/* Notify listeners. */
this.listeners.forEach(l => (l(this.value)));
}
/* Sync value from form checkboxes. */
sync()
{
const dataSources = document.querySelectorAll('#data-property-selector');
const value = this.value;
dataSources.forEach(dataSource =>
{
const group = dataSource.getAttribute('object-group');
const items = dataSource.querySelectorAll('input[type="checkbox"]');
items.forEach(item =>
{
const itemPropName = item.getAttribute('prop');
if(itemPropName.includes('.'))
{
if(item.checked)
{
if(!value.includes(itemPropName)) value.push(itemPropName);
}
else if(value.includes(itemPropName)) value.splice(value.indexOf(itemPropName), 1);
}
});
});
this.value = value;
document.querySelector('#datasource').value = value.join(',');
/* Notify listeners. */
this.listeners.forEach(l => (l(this.value)));
}
/* Subscribe for store value changes. */
subscribe(listener)
{
this.listeners.push(listener);
}
/* Unsubscribe for store value changes. */
unsubscribe(listener)
{
this.listeners = this.listeners.filter(l => l !== listener);
}
}
window.dataSourceStore = new DataSourceStore(); // Create store instance.
/* Data property selector component, declared as custom element. */
class DataPropertySelector extends HTMLDivElement
{
/* Render with given options. */
render(options)
{
this.innerHTML = '';
/* Get options. */
const {group} = options;
/* Get data source. */
const dataSource = window.dataSource[group];
const dataSourceLang = window.dataSourceLang[group];
/* Render data source. */
for(const [source, props] of Object.entries(dataSource))
{
const objView = document.createElement('div');
objView.classList.add('obj-view');
const headerItem = new DataPropertyCheckbox();
headerItem.classList.add('obj-view-header');
headerItem.setAttribute('source', source);
headerItem.setAttribute('prop', group);
objView.appendChild(headerItem);
const objViewBody = document.createElement('div');
objViewBody.classList.add('obj-view-body');
props.forEach(prop =>
{
const item = new DataPropertyCheckbox();
item.classList.add('obj-view-item');
item.setAttribute('source', source);
item.setAttribute('group', group);
item.setAttribute('prop', prop);
objViewBody.appendChild(item);
});
objView.appendChild(objViewBody);
this.appendChild(objView);
}
}
/* Handle attr change, rerender. */
attributeChangedCallback(name, oldValue, newValue)
{
if(name === 'object-group' && oldValue !== newValue) this.render({group: newValue});
}
/* Define observed attr. */
static get observedAttributes()
{
return ['object-group'];
}
}
customElements.define('data-property-selector', DataPropertySelector, {extends: 'div'});
/*
* Checkbox with label for data selection, declared as custom element.
*
* @prop {string} group - Data source group name (selected from left sidebar).
* @prop {string} source - Data source category (sub-group).
* @prop {string} prop - Data source property name.
*/
class DataPropertyCheckbox extends HTMLDivElement
{
/* Render checkbox with label, listen for events. */
render()
{
this.innerHTML = '';
const prop = this.getAttribute('prop');
const source = this.getAttribute('source');
const group = this.getAttribute('group');
this.classList.add('checkbox-primary', 'checkbox-inline');
const itemOption = document.createElement('input');
itemOption.setAttribute('type', 'checkbox');
itemOption.setAttribute('prop', !!group ? `${source}.${prop}` : source);
this.appendChild(itemOption);
const itemLabel = document.createElement('label');
itemLabel.appendChild(document.createTextNode(!!group ? dataSourceLang[group][source][prop] : dataSourceLang[prop][source].common));
/* Setup event listeners. */
itemLabel.addEventListener('click', (e =>
{
itemOption.checked = !itemOption.checked;
this.handleCheck(itemOption.checked);
}).bind(this));
itemOption.addEventListener('change', (e =>
{
this.handleCheck(itemOption.checked);
}).bind(this));
this.appendChild(itemLabel);
}
/* Handle check all for checkbox group, bidirectionally. And push selections into datasource input. */
handleCheck(newValue)
{
if(this.classList.contains('obj-view-header'))
{
const objViewBody = this.parentElement.querySelector('.obj-view-body');
for(const item of objViewBody.querySelectorAll('.obj-view-item input[type="checkbox"]')) item.checked = newValue;
}
else
{
const objViewBody = this.parentElement;
const headerItem = objViewBody.parentElement.querySelector('.obj-view-header input[type="checkbox"]');
const groupItems = objViewBody.querySelectorAll('.obj-view-item input[type="checkbox"]');
let allChecked = true;
for(const item of groupItems)
{
if(!item.checked) allChecked = false;
}
headerItem.checked = allChecked;
}
window.dataSourceStore.sync();
}
/* Handle attr change, rerender. */
attributeChangedCallback()
{
/* Skip rendering if props not complete. */
if(!this.hasAttribute('prop') || !this.hasAttribute('source')) return;
this.render();
}
/* Define observed attr. */
static get observedAttributes()
{
return ['group', 'source', 'prop'];
}
}
customElements.define('data-property-checkbox', DataPropertyCheckbox, {extends: 'div'});
/* Selected data column title, dynamically render with current group and selection. */
class SelectedTitleText extends HTMLSpanElement
{
/* Setup re-render trigger. */
constructor()
{
super();
window.dataSourceStore.subscribe(this.render.bind(this));
}
/* Render span from string format. */
render()
{
this.innerHTML = '';
const format = this.getAttribute('format');
const group = this.getAttribute('group');
const count = window.dataSourceStore.value.length;
const args = [dataSourceLang[group].common, count];
this.innerHTML = format.replace(/{(\d+)}/g, ((match, argIndex) =>
{
return typeof args[argIndex] !== 'undefined' ? args[argIndex] : match;
}));
}
/* Handle attr change, rerender. */
attributeChangedCallback(name, oldValue, newValue)
{
if(name === 'group' && oldValue !== newValue) this.render();
}
/* Define observed attr. */
static get observedAttributes()
{
return ['group'];
}
}
customElements.define('selected-title-text', SelectedTitleText, {extends: 'span'});
/* Sortable selected data column list, dynamically render with current selection. */
class SelectedDataSorter extends HTMLDivElement
{
/* Setup re-render trigger. */
constructor()
{
super();
window.dataSourceStore.subscribe(this.render.bind(this));
}
/* Render sortable list from current selection. */
render()
{
this.innerHTML = '';
const group = this.getAttribute('group');
const props = window.dataSourceStore.value;
const listView = document.createElement('ol');
listView.classList.add('list-group');
props.forEach(prop =>
{
const item = document.createElement('li');
item.classList.add('list-group-item');
item.setAttribute('prop', prop);
const dragIcon = document.createElement('i');
dragIcon.classList.add('icon', 'icon-move', 'text-gray', 'drag-icon');
item.appendChild(dragIcon);
const [source, propName] = prop.split('.');
const itemText = document.createElement('span');
itemText.appendChild(document.createTextNode(`${dataSourceLang[group][source].common} / ${dataSourceLang[group][source][propName]}`));
item.appendChild(itemText);
const itemRemove = document.createElement('i');
itemRemove.classList.add('icon', 'icon-close', 'text-gray', 'remove-icon');
itemRemove.addEventListener('click', (() =>
{
window.dataSourceStore.value = window.dataSourceStore.value.filter(p => p !== prop);
document.querySelector(`.obj-view-item input[type="checkbox"][prop="${prop}"]`).click();
}).bind(this));
item.appendChild(itemRemove);
listView.appendChild(item);
});
this.appendChild(listView);
/* Setup sortable on this list. */
$(listView).sortable(
{
handle: '.list-group-item',
finish: () =>
{
const props = [];
for(const item of listView.querySelectorAll('.list-group-item')) props.push(item.getAttribute('prop'));
window.dataSourceStore.value = props;
window.dataSourceStore.sync();
}
});
}
/* Handle attr change, rerender. */
attributeChangedCallback(name, oldValue, newValue)
{
if(name === 'group' && oldValue !== newValue) this.render();
}
/* Define observed attr. */
static get observedAttributes()
{
return ['group'];
}
}
customElements.define('selected-data-sorter', SelectedDataSorter, {extends: 'div'});
</script>
<?php include 'promptdesignprogressbar.html.php';?>
<div id='mainContent' class='main-content' style='height: calc(100vh - 120px);'>
<form id="mainForm" onsubmit="return validateForm();" class='load-indicator main-form form-ajax' method='post' style='height: 100%;'>
<div class='center-wrapper'>
<div class='center-content'>
<div id='data-selector'>
<div id='data-sources'>
<div id='data-categories'>
<div class='heading text-center'>
<h4><?php echo $lang->ai->prompts->selectDataSource;?></h4>
</div>
<div id='data-category-select'>
<ul>
<?php foreach(array_keys($dataSource) as $index => $objectGroupKey):?>
<li data-group-key='<?php echo $objectGroupKey;?>'>
<a class='btn btn-block <?php echo $objectGroupKey == $activeDataSource ? ' active btn-info' : ' btn-link';?>' <?php echo $objectGroupKey == $activeDataSource ? 'style="background-color: #E6F0FF;"' : '';?>>
<?php echo $lang->ai->dataSource[$objectGroupKey]['common'];?>
</a>
</li>
<?php endforeach;?>
</ul>
</div>
</div>
<div id='data-properties'>
<div class='heading'>
<h4><?php echo $lang->ai->prompts->selectData;?></h4>
<small class='text-gray'><?php echo $lang->ai->prompts->selectDataTip;?></small>
</div>
<div id='data-property-select'>
<div id='data-property-selector' is='data-property-selector' object-group='<?php echo $activeDataSource;?>'></div>
</div>
</div>
</div>
<div id='data-selected'>
<div class='heading'>
<h4><span id='selected-title-text' is='selected-title-text' group='<?php echo $activeDataSource;?>' format='<?php echo $lang->ai->prompts->selectedFormat;?>'></span></h4>
</div>
<div id='data-selected-items'>
<div id='selected-data-sorter' is='selected-data-sorter' group='<?php echo $activeDataSource;?>'></div>
</div>
</div>
</div>
<div style='display: flex; flex-grow: 1; flex-direction: column-reverse;'>
<?php echo html::hidden('datasource', $prompt->source);?>
<?php echo html::hidden('datagroup', $activeDataSource);?>
<div style='display: flex; justify-content: center;'><?php echo html::submitButton($lang->ai->nextStep, 'disabled name="jumpToNext" value="1"');?></div>
</div>
</div>
</div>
</form>
</div>
<script>
function validateForm()
{
let pass = true;
const dataGroup = document.querySelector('input[name="datagroup"]')?.value;
const dataSource = document.querySelector('input[name="datasource"]')?.value;
if(!dataGroup || !dataSource)
{
$.zui.messager.danger('<?php echo sprintf($lang->ai->validate->noEmpty, $lang->ai->prompts->selectDataSource);?>');
pass = false;
}
if(pass)
{
/* Disable checkboxes to prevent them getting posted as form data. */
$('#data-property-selector input[type="checkbox"]').each(function() {$(this).attr('disabled', 'disabled');});
}
return pass;
}
$(function()
{
/* Handle category switching. */
$('#data-category-select > ul > li > a').click(function()
{
if($(this).hasClass('active')) return;
/* Reset selected data. */
window.dataSourceStore.reset();
/* Update menu states. */
$('#data-category-select > ul > li > a').removeClass('active btn-info').addClass('btn-link');
$(this).addClass('active btn-info').removeClass('btn-link');
var currentGroup = $(this).parent().attr('data-group-key');
/* Update other component props. */
$('#data-property-selector').attr('object-group', currentGroup);
$('#selected-title-text').attr('group', currentGroup);
$('#selected-data-sorter').attr('group', currentGroup);
/* Update hidden form fields. */
$('input[name="datagroup"]').val(currentGroup);
});
/* Toggle disabled submit button. */
window.dataSourceStore.subscribe(function(value)
{
$('#submit').attr('disabled', !value.length);
});
<?php if($prompt->source):?>
/* Init checkboxes states. */
'<?php echo $prompt->source;?>'.split(',').filter(function(p) {return p;}).forEach(function(prop)
{
$('#data-property-selector input[type="checkbox"][prop="' + prop + '"]').click();
});
<?php endif;?>
});
</script>
<?php include '../../common/view/footer.html.php';?>
-167
View File
@@ -1,167 +0,0 @@
<?php
/**
* The ai prompt set purpose view file of ai module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Wenrui LI <liwenrui@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<style>
.center-wrapper {display: flex; justify-content: center; height: 100%;}
.center-content {width: 100%; height: 100%; display: flex; flex-direction: column;}
#purpose-setter {display: flex; flex-direction: row; max-height: calc(100% - 32px);}
#purpose-setter > div {flex-grow: 1; flex-basis: 0; padding: 0px 12px;}
.content-wrapper {padding: 0 12px; overflow-x: hidden; overflow-y: auto; scrollbar-gutter: stable;}
.content-row {display: flex; flex-direction: row; padding: 8px 0px;}
.input-label {width: 120px; padding: 6px 12px; text-align: right;}
.input {flex-grow: 1;}
#prompt-preview-wrapper {margin: 16px 0; height: calc(100% - 48px);}
#prompt-preview {padding: 8px; border: 1px solid #E6EAF1; border-radius: 4px; background-color: #f8f8f8; min-height: 100px; height: 100%; overflow-y: auto;}
#prompt-preview {cursor: default; user-select: none;}
#prompt-preview .active {background-color: #d6e5fe;}
#prompt-preview .prompt-data, #prompt-preview .prompt-role, #prompt-preview .prompt-text {border-bottom: 1px solid #E6EAF1; padding: 16px 0;}
#prompt-preview .prompt-data {padding-top: 0;}
#prompt-preview .prompt-text {border-bottom: unset; padding-bottom: 0;}
#prompt-preview .block-header {padding-bottom: 8px;}
#prompt-preview .block-content > div + div {margin-top: 4px;}
#prompt-preview .prompt-text-part + .prompt-text-part {margin-top: 4px;}
#prompt-previewer {font-weight: bold;}
</style>
<script>
class PromptPreviewer extends HTMLDivElement
{
connectedCallback()
{
if(this.isConnected)
{
['purpose', 'elaboration'].forEach(id =>
{
['input', 'focus', 'blur'].forEach(event => document.getElementById(id).addEventListener(event, this.updatePromptView));
});
}
this.render();
}
render()
{
this.innerHTML = '';
/* Create textarea input preview. */
['purpose', 'elaboration'].forEach(id =>
{
const contentView = document.createElement('div');
contentView.id = `${id}-preview`;
contentView.classList.add('prompt-text-part');
contentView.innerHTML = document.getElementById(id).value;
if(id === document.activeElement.id) contentView.classList.add('active');
this.appendChild(contentView);
});
}
updatePromptView()
{
/* Sync textarea input preview. */
['purpose', 'elaboration'].forEach(id =>
{
const contentView = document.getElementById(`${id}-preview`);
contentView.innerHTML = document.getElementById(id).value;
if(id === document.activeElement.id)
{
contentView.classList.add('active');
}
else
{
contentView.classList.remove('active');
}
});
}
}
customElements.define('prompt-previewer', PromptPreviewer, {extends: 'div'});
</script>
<?php include 'promptdesignprogressbar.html.php';?>
<div id='mainContent' class='main-content' style='height: calc(100vh - 120px);'>
<form id="mainForm" onsubmit="return validateForm();" class='load-indicator main-form form-ajax' method='post' style='height: 100%;'>
<div class='center-wrapper'>
<div class='center-content'>
<div id='purpose-setter'>
<div id='purpose-input'>
<h4><?php echo $lang->ai->prompts->purpose;?></h4>
<div class='content-wrapper'>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->purpose;?></span></div>
<div class='input'><?php echo html::textarea('purpose', $prompt->purpose, "class='form-control' rows='6' placeholder='{$lang->ai->prompts->purposeTip}' required");?></div>
</div>
<div class='content-row'>
<div class='input-label'><span><?php echo $lang->ai->prompts->elaboration;?></span></div>
<div class='input'><?php echo html::textarea('elaboration', $prompt->elaboration, "class='form-control' rows='6' placeholder='{$lang->ai->prompts->elaborationTip}'");?></div>
</div>
</div>
</div>
<div>
<h4><?php echo $lang->ai->prompts->inputPreview;?></h4>
<div id='prompt-preview-wrapper'>
<div id='prompt-preview'>
<div class='prompt-data'>
<div class='block-header text-gray'><?php echo $lang->ai->prompts->dataPreview;?></div>
<div class='block-content code' style='white-space: pre-wrap; word-break: break-word;'><?php echo $dataPreview;?></div>
</div>
<div class='prompt-role'>
<div class='block-header text-gray'><?php echo $lang->ai->prompts->rolePreview;?></div>
<div class='block-content'>
<div><?php echo $prompt->role;?></div>
<div><?php echo $prompt->characterization;?></div>
</div>
</div>
<div class='prompt-text'>
<div class='block-header text-gray'><?php echo $lang->ai->prompts->promptPreview;?></div>
<div id='prompt-previewer' is='prompt-previewer'></div>
</div>
</div>
</div>
</div>
</div>
<div style='display: flex; flex-grow: 1; flex-direction: column-reverse;'>
<div style='display: flex; justify-content: center;'><?php echo html::submitButton($lang->ai->nextStep, 'disabled name="jumpToNext" value="1"');?></div>
</div>
</div>
</div>
</form>
</div>
<script>
function validateForm()
{
let pass = true;
const purpose = document.getElementById('purpose')?.value;
if(!purpose)
{
pass = false;
$.zui.messager.danger('<?php echo sprintf($lang->ai->validate->noEmpty, $lang->ai->prompts->purpose);?>');
}
return pass;
}
$(function()
{
$('#purpose').on('input', function()
{
const val = $(this).val();
if(val.length > 0)
{
$('#submit').removeAttr('disabled');
}
else
{
$('#submit').attr('disabled', 'disabled');
}
});
$('#purpose').trigger('input');
});
</script>
<?php include '../../common/view/footer.html.php';?>
-192
View File
@@ -1,192 +0,0 @@
<?php
/**
* The ai prompt target form select view file of ai module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Wenrui LI <liwenrui@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<style>
.center-wrapper {display: flex; justify-content: center; height: 100%;}
.center-content {width: 100%; height: 100%; display: flex; flex-direction: column;}
#select-form {display: flex; flex-direction: row; max-height: calc(100% - 32px);}
#select-form > div {flex-grow: 1; flex-basis: 0; padding: 0px 12px;}
.content-row {display: flex; flex-direction: row; padding: 8px 0px;}
.input-label {width: 120px; padding: 6px 12px; text-align: right;}
.input {flex-grow: 1;}
#prompt-preview-wrapper {margin: 16px 0; height: calc(100% - 48px);}
#prompt-preview {padding: 8px; border: 1px solid #E6EAF1; border-radius: 4px; background-color: #f8f8f8; min-height: 100px; height: 100%; overflow-y: auto; cursor: default; user-select: none;}
#prompt-preview .active {background-color: #d6e5fe;}
#prompt-preview .prompt-data, #prompt-preview .prompt-role, #prompt-preview .prompt-text {border-bottom: 1px solid #E6EAF1; padding: 16px 0;}
#prompt-preview .prompt-data {padding-top: 0;}
#prompt-preview .prompt-text {border-bottom: unset; padding-bottom: 0;}
#prompt-preview .block-header {padding-bottom: 8px;}
#prompt-preview .block-content > div + div {margin-top: 4px;}
#prompt-preview .prompt-text-part + .prompt-text-part {margin-top: 4px;}
#prompt-previewer {font-weight: bold;}
#form-selector .header > * {display: inline-block;}
#form-selector .content {margin: 6px 0; max-height: calc(100% - 47px); overflow-y: auto; border: 1px solid #E6EAF1; border-left: none;}
.target-form-group {display: grid; grid-template-columns: 120px 1fr; grid-gap: 8px; border: 1px solid #E6EAF1; border-right: 0;}
.target-form-group:first-of-type {border-top: none;}
.target-form-group:last-of-type {border-bottom: none;}
.target-form-group + .target-form-group {border-top: unset;}
.target-form-group .header {display: flex; align-items: center; padding: 0 12px; background-color: #f8f8f8;}
.target-form-group .options {display: grid; padding: 12px 16px; grid-template-columns: repeat(4, 1fr);}
.target-form-group .option {padding: 4px 0;}
.target-form-group .option label {cursor: pointer;}
.target-form-group .option input {cursor: pointer;}
#go-test-btn {margin-left: 16px;}
@media (max-width: 1366px)
{
.target-form-group .options {grid-template-columns: repeat(3, 1fr);}
}
</style>
<?php include 'promptdesignprogressbar.html.php';?>
<div id='mainContent' class='main-content' style='height: calc(100vh - 120px);'>
<form id="mainForm" onsubmit="return validateForm();" class='load-indicator main-form form-ajax' method='post' style='height: 100%;'>
<div class='center-wrapper'>
<div class='center-content'>
<div id='select-form'>
<div id='form-selector'>
<div class='header'>
<h4><?php echo $lang->ai->prompts->selectTargetForm;?></h4>
<small class='text-gray'><?php echo $lang->ai->prompts->selectTargetFormTip;?></small>
</div>
<div class='content'>
<?php foreach ($config->ai->targetForm as $name => $forms):?>
<div class='target-form-group'>
<div class='header text-gray'>
<div><?php echo $lang->ai->targetForm[$name]['common'];?></div>
</div>
<div class='options'>
<?php foreach(array_keys($forms) as $form):?>
<div class='option'>
<input type='radio' name='targetForm' value='<?php echo "$name.$form";?>' <?php echo "$name.$form" == $prompt->targetForm ? 'checked' : '';?>>
<label><?php echo $lang->ai->targetForm[$name][$form];?></label>
</div>
<?php endforeach;?>
</div>
</div>
<?php endforeach;?>
</div>
</div>
<div>
<h4><?php echo $lang->ai->prompts->inputPreview;?></h4>
<div id='prompt-preview-wrapper'>
<div id='prompt-preview'>
<div class='prompt-data'>
<div class='block-header text-gray'><?php echo $lang->ai->prompts->dataPreview;?></div>
<div class='block-content code' style='white-space: pre-wrap; word-break: break-word;'><?php echo $dataPreview;?></div>
</div>
<div class='prompt-role'>
<div class='block-header text-gray'><?php echo $lang->ai->prompts->rolePreview;?></div>
<div class='block-content'>
<div><?php echo $prompt->role;?></div>
<div><?php echo $prompt->characterization;?></div>
</div>
</div>
<div class='prompt-text'>
<div class='block-header text-gray'><?php echo $lang->ai->prompts->promptPreview;?></div>
<div class='block-content'>
<div><?php echo $prompt->purpose;?></div>
<div><?php echo $prompt->elaboration;?></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div style='display: flex; flex-grow: 1; flex-direction: column-reverse;'>
<div style='display: flex; justify-content: center;'>
<?php echo html::submitButton($lang->ai->nextStep, 'disabled name="jumpToNext" value="1"');?>
<button type='submit' name='goTesting' value='1' id='go-test-btn' disabled class='btn btn-wide btn-secondary'><?php echo $lang->ai->goTesting;?></button>
</div>
</div>
</div>
</div>
</form>
</div>
<script>
function validateForm()
{
let pass = false;
const radios = document.getElementsByName('targetForm');
for(let radio of radios)
{
if(radio.checked)
{
pass = true;
break;
}
}
if(!pass)
{
$.zui.messager.danger('<?php echo sprintf($lang->ai->validate->noEmpty, $lang->ai->prompts->selectTargetForm);?>');
}
return pass;
}
$(function()
{
$('.target-form-group .option label').click(function()
{
$(this).parent().find('input').click();
});
$('.target-form-group .option input').change(function()
{
var $selected = $('.target-form-group').find('.option input:checked');
if($selected.length > 0)
{
$('#submit').removeAttr('disabled');
$('#go-test-btn').removeAttr('disabled');
}
});
$('.target-form-group .option input:checked').trigger('change');
/* Enable buttons after going testing. */
$('#mainForm').ajaxForm({
success: function(response, status)
{
var message = response.msg;
var location = response.locate;
var target = response.target ? response.target : '#submit';
if(message)
{
var $targetSubmitBtn = $(target);
if($targetSubmitBtn.length)
{
$targetSubmitBtn.popover({
container: 'body',
trigger: 'manual',
content: message,
tipClass: 'popover-in-modal popover-success popover-form-result',
placement: 'right',
}).popover('show');
setTimeout(function() {$targetSubmitBtn.popover('destroy');}, 2000);
}
}
},
finish: function(response, _, $form)
{
if(response.locate) setTimeout(function()
{
var $a = $('<a href="' + response.locate + '" target="_self"></a>');
$a.appendTo('body');
if($a.length) $a[0].click();
$a.remove();
}, 1200);
$form.enableForm();
}
});
});
</script>
<?php include '../../common/view/footer.html.php';?>
-26
View File
@@ -1,26 +0,0 @@
<?php
/**
* The role template list component file of AI module of ZenTaoPMS.
*
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Jianyu Chen <chenjianyu@easycorp.ltd>
* @package ai
* @link https://www.zentao.net
*/
?>
<div id="roleList" style="display: flex; flex-direction: column; gap:8px;">
<?php foreach($roleTemplates as $role):?>
<div id="role-template-<?php echo $role->id;?>" data-id="<?php echo $role->id;?>" class="role-template-card" style="border: 1px solid #D8DBDE; border-radius: 4px; padding: 12px; width: 100%;">
<div style="display: flex; justify-content: space-between; align-items: center; gap: 16px;">
<p id="role-<?php echo $role->id;?>" class="role" style="overflow: hidden; white-space: nowrap; text-overflow: clip;" title="<?php echo $role->role; ?>"><?php echo $role->role; ?></p>
<div style="display: flex; gap: 2px;">
<?php echo html::commonButton("<span class='text-primary'>{$lang->app->common}</span>", "data-action='apply'", 'btn btn-link'); ?>
<?php echo html::commonButton("<i class='icon icon-edit icon-sm text-primary'></i>", "data-action='edit'", 'btn btn-link'); ?>
<?php echo html::commonButton("<i class='icon icon-trash icon-sm text-primary'></i>", "data-action='del'", 'btn btn-link'); ?>
</div>
</div>
<p id="characterization-<?php echo $role->id;?>" class="characterization text-gray" style="overflow: hidden; white-space: nowrap; text-overflow: clip; padding: 4px 0;" title="<?php echo $role->characterization;?>"><?php echo $role->characterization; ?></p>
</div>
<?php endforeach;?>
</div>