Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: multiple prompts #602

Merged
merged 77 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
7e90f11
feat: add basic repeater field html and js
ravinderk Oct 19, 2023
d5fb580
refactor: add new function to render prompt repater field
ravinderk Oct 19, 2023
61d9013
feat: use prompt repeater field to render excerpt prompt field
ravinderk Oct 19, 2023
8ca67ed
fix: render atleast one prompt field
ravinderk Oct 19, 2023
6aff67a
fix: attach event to existing fieldset
ravinderk Oct 19, 2023
ac9c1c6
fix: set default value to prompt repeater field
ravinderk Oct 19, 2023
39c4123
fix: reset set default button
ravinderk Oct 19, 2023
312b5d7
fix: prevent fieldset template and reuse when create new fieldset
ravinderk Oct 19, 2023
7d47e45
feat: add option to select fields
ravinderk Oct 19, 2023
107fa11
Merge branch 'develop' into feat/multiple-prompts-setting-field
ravinderk Oct 19, 2023
cfaebfe
feat: add prompt repeater field to other settings
ravinderk Oct 20, 2023
b866763
fix: only de select option in active setting row
ravinderk Oct 20, 2023
0d03b52
fix: make setting name field dynamic
ravinderk Oct 20, 2023
311708e
Added prompt sanitization before save it to options.
iamdharmesh Oct 20, 2023
2a68206
Use user selected default prompt in request to OpenAI.
iamdharmesh Oct 20, 2023
137b371
fix: add unique index to form fields
ravinderk Oct 20, 2023
f1f3623
format: improve code formatting
ravinderk Oct 20, 2023
c36781a
fix: add js specific class to input field
ravinderk Oct 20, 2023
f484ccf
fix: remove unused textarea render callback
ravinderk Oct 20, 2023
fa14400
format: improve code formatting
ravinderk Oct 20, 2023
ba08731
change: store default as interger
ravinderk Oct 20, 2023
6eab7e4
fix: check only for default value
ravinderk Oct 20, 2023
2a0537d
refactor: reduce unnessary function argument
ravinderk Oct 20, 2023
1ffdb4a
doc: add phpdoc since tag
ravinderk Oct 20, 2023
33276de
fix: remove duplicate code
ravinderk Oct 20, 2023
921f1fb
chore: revert commit
ravinderk Oct 20, 2023
948148a
fix: sanitize prompts data
ravinderk Oct 20, 2023
60825a6
Add max-width to repeater field.
iamdharmesh Oct 20, 2023
06c8a13
feat: take admin confirmation before remove prompt
ravinderk Oct 20, 2023
7cee31e
fix: add event to newply created button
ravinderk Oct 20, 2023
8c71b56
fix: hide remove action if prompt count is one
ravinderk Oct 20, 2023
eb71613
format: fix typo
ravinderk Oct 20, 2023
16cbeeb
fix: get admin js file asset and version from asset.php
ravinderk Oct 20, 2023
9fd9a48
refactor: move code to script file
ravinderk Oct 20, 2023
9f763cf
format: improve code formatting
ravinderk Oct 20, 2023
f369854
format: use null coalescing operator
ravinderk Oct 20, 2023
8853651
doc: add missing tag
ravinderk Oct 20, 2023
f7d16d1
chore: remove package.json change
ravinderk Oct 20, 2023
320b75e
fix: use correct function type
ravinderk Oct 20, 2023
f6a3bae
Merge branch 'develop' into feat/multiple-prompts-setting-field
dkotter Oct 20, 2023
119d7c3
Update tests
dkotter Oct 20, 2023
45b0251
Add tests to ensure that setting custom prompts returns different dat…
dkotter Oct 20, 2023
02d11f5
Add tests to verify that adding multiple prompts, deleting a prompt a…
dkotter Oct 20, 2023
7b47b43
Force clicks when setting defaults or removing prompts
dkotter Oct 20, 2023
e3394f9
Update includes/Classifai/Providers/OpenAI/ChatGPT.php
ravinderk Oct 21, 2023
f6a1123
Update includes/Classifai/Providers/Provider.php
ravinderk Oct 21, 2023
ef3f117
Update src/js/admin.js
ravinderk Oct 21, 2023
4f64f32
Update includes/Classifai/Providers/OpenAI/ChatGPT.php
ravinderk Oct 21, 2023
812f9aa
Update src/js/admin.js
ravinderk Oct 21, 2023
672660b
Update src/js/admin.js
ravinderk Oct 21, 2023
3129659
Update src/js/admin.js
ravinderk Oct 21, 2023
2ed8fa5
Update includes/Classifai/Providers/OpenAI/ChatGPT.php
ravinderk Oct 21, 2023
72c5e5e
Update includes/Classifai/Providers/OpenAI/ChatGPT.php
ravinderk Oct 21, 2023
b0974d0
Update includes/Classifai/Providers/Provider.php
ravinderk Oct 21, 2023
7c975e1
format: update variable name
ravinderk Oct 21, 2023
0fd59ae
fix: set first prompt as default prompt
ravinderk Oct 21, 2023
0b6164b
fix: validate url param before using them
ravinderk Oct 21, 2023
d67de70
refactor: utilize existing function to load admin assets
ravinderk Oct 21, 2023
1648bdb
Update includes/Classifai/Providers/OpenAI/ChatGPT.php
ravinderk Oct 24, 2023
1fb3f7f
Update includes/Classifai/Providers/OpenAI/ChatGPT.php
ravinderk Oct 24, 2023
158dc2f
Merge branch 'develop' into feat/multiple-prompts-setting-field
ravinderk Nov 1, 2023
b0ec0fe
feat: replace prompt action button icons with text
ravinderk Nov 1, 2023
7201279
format: improve code formatting
ravinderk Nov 1, 2023
7cb17ba
feat: add click event handle to action row items
ravinderk Nov 1, 2023
b53b02c
format: improve code formatting
ravinderk Nov 1, 2023
685937c
feat: make field required
ravinderk Nov 1, 2023
b9ba4c6
feat: display setting field as required
ravinderk Nov 1, 2023
631fdb0
fix: add default prompt to all prompt setting fields
ravinderk Nov 1, 2023
d12674b
feat: assign placeholder to each prompt setting field
ravinderk Nov 1, 2023
49156fc
change: update setting label
ravinderk Nov 1, 2023
e511fdc
fix: update action item content
ravinderk Nov 2, 2023
7fff7a5
change: update rest api endpoint description
ravinderk Nov 2, 2023
5d71866
tests: fix broken e2e tests
ravinderk Nov 2, 2023
419163b
tests: fix borken e2e test
ravinderk Nov 2, 2023
1384bf2
Some text standardization changes
dkotter Nov 2, 2023
fc2c4e9
Don't set our default prompts as the input values, as we don't want t…
dkotter Nov 2, 2023
533bbb1
Merge branch 'develop' into feat/multiple-prompts-setting-field
dkotter Nov 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
347 changes: 234 additions & 113 deletions includes/Classifai/Providers/OpenAI/ChatGPT.php

Large diffs are not rendered by default.

84 changes: 69 additions & 15 deletions includes/Classifai/Providers/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,27 +230,81 @@ class="<?php echo esc_attr( $class ); ?>"
}

/**
* Generic textarea field callback
* Generic prompt repeater field callback
*
* @since 2.4.0
*
* @param array $args The args passed to add_settings_field.
*/
public function render_textarea( $args ) {
$option_index = isset( $args['option_index'] ) ? $args['option_index'] : false;
$setting_index = $this->get_settings( $option_index );
$value = ( isset( $setting_index[ $args['label_for'] ] ) ) ? $setting_index[ $args['label_for'] ] : '';
$class = isset( $args['class'] ) ? $args['class'] : 'large-text';
$placeholder = isset( $args['placeholder'] ) ? $args['placeholder'] : '';
public function render_prompt_repeater_field( array $args ): void {
$option_index = $args['option_index'] ?? false;
$setting_index = $this->get_settings( $option_index );
$value = $setting_index[ $args['label_for'] ] ?? '';
$class = $args['class'] ?? 'large-text';
$placeholder = $args['placeholder'] ?? '';
$field_name_prefix = sprintf(
'classifai_%1$s%2$s[%3$s]',
$this->option_name,
$option_index ? "[$option_index]" : '',
$args['label_for']
);

// Check for a default value
$value = ( empty( $value ) && isset( $args['default_value'] ) ) ? $args['default_value'] : $value;

$prompt_count = count( $value );
$field_index = 0;
?>
<textarea
id="<?php echo esc_attr( $args['label_for'] ); ?>"
class="<?php echo esc_attr( $class ); ?>"
rows="4"
name="classifai_<?php echo esc_attr( $this->option_name ); ?><?php echo $option_index ? '[' . esc_attr( $option_index ) . ']' : ''; ?>[<?php echo esc_attr( $args['label_for'] ); ?>]"
placeholder="<?php echo esc_attr( $placeholder ); ?>"
><?php echo esc_textarea( $value ); ?></textarea>

<?php foreach ( $value as $prompt ) : ?>
<?php $is_default_prompt = 1 === $prompt['default']; ?>

<fieldset class="classifai-field-type-prompt-setting">
<input type="hidden"
name="<?php echo esc_attr( $field_name_prefix . "[$field_index][default]" ); ?>"
value="<?php echo esc_attr( $prompt['default'] ?? '' ); ?>"
class="js-setting-field__default">
<label>
<?php esc_html_e( 'Title', 'classifai' ); ?>&nbsp;*
<span class="dashicons dashicons-editor-help"
title="<?php esc_attr_e( 'Short description of prompt to use for identification', 'classifai' ); ?>"></span>
<input type="text"
name="<?php echo esc_attr( $field_name_prefix . "[$field_index][title]" ); ?>"
placeholder="<?php esc_attr_e( 'Prompt title', 'classifai' ); ?>"
value="<?php echo esc_attr( $prompt['title'] ?? '' ); ?>"
required>
</label>

<label>
<?php esc_html_e( 'Prompt', 'classifai' ); ?>
<textarea
class="<?php echo esc_attr( $class ); ?>"
rows="4"
name="<?php echo esc_attr( $field_name_prefix . "[$field_index][prompt]" ); ?>"
placeholder="<?php echo esc_attr( $placeholder ); ?>"
><?php echo esc_textarea( $prompt['prompt'] ?? '' ); ?></textarea>
</label>

<div class="actions-rows">
<a href="#" class="action__set_default <?php echo $is_default_prompt ? 'selected' : ''; ?>">
<?php if ( $is_default_prompt ) : ?>
<?php esc_html_e( 'Default Prompt', 'classifai' ); ?>
<?php else : ?>
<?php esc_html_e( 'Set as default prompt', 'classifai' ); ?>
<?php endif; ?>
</a>
<a href="#" class="action__remove_prompt" style="<?php echo 1 === $prompt_count ? 'display:none;' : ''; ?>">
<?php esc_html_e( 'Trash', 'classifai' ); ?>
</a>
</div>
</fieldset>
<?php ++$field_index; ?>
<?php endforeach; ?>

<button
class="button-secondary js-classifai-add-prompt-fieldset">
<?php esc_html_e( 'Add new prompt', 'classifai' ); ?>
</button>

<?php
if ( ! empty( $args['description'] ) ) {
echo '<br /><span class="description classifai-input-description">' . wp_kses_post( $args['description'] ) . '</span>';
Expand Down
2 changes: 1 addition & 1 deletion includes/Classifai/Services/LanguageProcessing.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ public function register_endpoints() {
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
'description' => esc_html__( 'The type of resize operation. "grow" or "shrink".', 'classifai' ),
'description' => esc_html__( 'The type of resize operation. "expand" or "condense".', 'classifai' ),
],
],
]
Expand Down
224 changes: 224 additions & 0 deletions src/js/admin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* global ClassifAI */
import { __ } from '@wordpress/i18n';
import '../scss/admin.scss';
import tippy from 'tippy.js';
import 'tippy.js/dist/tippy.css';
Expand All @@ -9,6 +10,7 @@ document.addEventListener( 'DOMContentLoaded', function () {
if ( ! template ) {
return;
}

const container = document.createElement( 'div' );
container.appendChild( document.importNode( template.content, true ) );

Expand Down Expand Up @@ -78,3 +80,225 @@ document.addEventListener( 'DOMContentLoaded', function () {
$passwordFieldTitle.innerText = ClassifAI.api_password;
} );
} )();

( () => {
// Attach event to add new prompt button.
const $addNewPromptFieldsetButton = document.querySelectorAll(
'button.js-classifai-add-prompt-fieldset'
);
if ( $addNewPromptFieldsetButton.length ) {
$addNewPromptFieldsetButton.forEach( ( button ) => {
button.addEventListener( 'click', ( e ) => {
e.preventDefault();

addNewFieldSet( e.target.previousElementSibling );
} );
} );
}

// Attach event to existing prompt fieldsets.
const $promptFieldsets = document.querySelectorAll(
'.classifai-field-type-prompt-setting'
);
if ( $promptFieldsets.length ) {
$promptFieldsets.forEach( ( $promptFieldset ) => {
attachEventPromptFieldset( $promptFieldset );
} );
}

// ------------------
// Helper function
// ------------------

/**
* Reset all input fields in a fieldset.
*
* @since 2.4.0
* @param {Element} $fieldset
* @param {Element} $parentRow
*/
function resetInputFields( $fieldset, $parentRow ) {
const $allFieldsets = $parentRow.querySelectorAll( 'fieldset' );
const $lastFieldset = Array.from( $allFieldsets ).pop();
const highestFieldIndexOfFieldset = parseInt(
$lastFieldset.querySelector( 'input' ).name.match( /\d+/ ).pop()
);
const fields = $fieldset.querySelectorAll( 'input, textarea' );
const actionButtons = $fieldset.querySelectorAll(
'.actions-rows .action__set_default'
);

// Reset form fields.
fields.forEach( ( field ) => {
field.value = '';

// Add index to field name.
field.name = field.name.replace(
/(\d+)/g,
() => highestFieldIndexOfFieldset + 1
);
} );

// Reset action buttons.
actionButtons.forEach( ( button ) => {
button.classList.remove( 'selected' );
button.textContent = __( 'Set as default prompt', 'classifai' );
} );
}

/**
* Attach event to fieldset.
*
* @since 2.4.0
* @param {Element} $newPromptFieldset
*/
function attachEventPromptFieldset( $newPromptFieldset ) {
// Add event to remove prompt link
const $removePromptFieldsetLink = $newPromptFieldset.querySelector(
'a.action__remove_prompt'
);

$removePromptFieldsetLink.addEventListener( 'click', ( e ) => {
e.preventDefault();
displayPromptRemovalModal( e.target );
} );

// Add event to set as default link.
const $setAsDefaultLink = $newPromptFieldset.querySelector(
'a.action__set_default'
);

$setAsDefaultLink.addEventListener( 'click', ( e ) => {
e.preventDefault();

// If already selected, do nothing.
if ( e.target.classList.contains( 'selected' ) ) {
return;
}

// Remove selected class from all buttons.
const $settingRow = e.target.closest( 'tr' );
const $setAsDefaultLinks = $settingRow.querySelectorAll(
'.action__set_default'
);
$setAsDefaultLinks.forEach( ( link ) => {
// Update text.
if ( link.classList.contains( 'selected' ) ) {
link.textContent = __(
'Set as default prompt',
'classifai'
);
}

link.classList.remove( 'selected' );

link
.closest( 'fieldset' )
.querySelector( '.js-setting-field__default' ).value = '';
} );

// Set selected class.
e.target.classList.add( 'selected' );

e.target.textContent = __( 'Default Prompt', 'classifai' );

// Set default value.
$newPromptFieldset.querySelector(
'.js-setting-field__default'
).value = '1';
} );
}

/**
* Handle prompt removal modal.
*
* @since 2.4.0
* @param {Element} removePromptLink
*/
function displayPromptRemovalModal( removePromptLink ) {
jQuery( '#js-classifai--delete-prompt-modal' ).dialog( {
modal: true,
title: __( 'Remove Prompt', 'classifai' ),
width: 550,
buttons: [
{
text: __( 'Cancel', 'classifai' ),
class: 'button-secondary',
click() {
jQuery( this ).dialog( 'close' );
},
},
{
text: __( 'Remove', 'classifai' ),
class: 'button-primary',
click() {
const fieldset = removePromptLink.closest( 'fieldset' );
const fieldsetContainer = fieldset.parentElement;
const canResetPrompt =
fieldset.querySelector(
'.js-setting-field__default'
).value === '1';
const hasOnlySinglePrompt =
2 ===
fieldsetContainer.querySelectorAll( 'fieldset' )
.length;

fieldset.remove();

// Set first prompt in list as default.
if ( canResetPrompt ) {
const setAsDefaultButton =
fieldsetContainer.querySelector(
'fieldset .action__set_default'
);

setAsDefaultButton.click();
}

// Hide remove button if only single fieldset is left.
if ( hasOnlySinglePrompt ) {
fieldsetContainer.querySelector(
'.action__remove_prompt'
).style.display = 'none';
}

jQuery( this ).dialog( 'close' );
},
style: 'margin-left: 10px;',
},
],
} );
}

/**
* Add a new fieldset.
*
* @since 2.4.0
* @param {Element} $sibling
*
* @return {Element} $newPromptFieldset
*/
function addNewFieldSet( $sibling ) {
const $promptFieldsetTemplate = $sibling.parentElement.querySelector(
'.classifai-field-type-prompt-setting'
);

// Show remove button if fieldset is single.
if (
1 === $sibling.parentElement.querySelectorAll( 'fieldset' ).length
) {
$sibling.parentElement.querySelector(
'.action__remove_prompt'
).style.display = 'block';
}

const $newPromptFieldset = $promptFieldsetTemplate.cloneNode( true );

resetInputFields( $newPromptFieldset, $sibling.closest( 'tr' ) );
attachEventPromptFieldset( $newPromptFieldset );

$sibling.insertAdjacentElement( 'afterend', $newPromptFieldset );

return $newPromptFieldset;
}
} )();
Loading
Loading