Skip to content

Commit

Permalink
Add bits from #10833
Browse files Browse the repository at this point in the history
Co-authored-by: Jack Sleight <jacksleight@gmail.com>
  • Loading branch information
jasonvarga and jacksleight committed Nov 15, 2024
1 parent 89466b6 commit 089a76f
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 1 deletion.
2 changes: 2 additions & 0 deletions resources/js/bootstrap/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import Modal from '../components/Modal.vue';
import ConfirmationModal from '../components/modals/ConfirmationModal.vue';
import FavoriteCreator from '../components/FavoriteCreator.vue';
import KeyboardShortcutsModal from '../components/modals/KeyboardShortcutsModal.vue';
import FieldActionModal from '../components/field-actions/FieldActionModal.vue';
import ResourceDeleter from '../components/ResourceDeleter.vue';
import Stack from '../components/stacks/Stack.vue';
import StackTest from '../components/stacks/StackTest.vue';
Expand Down Expand Up @@ -142,6 +143,7 @@ Vue.component('confirmation-modal', ConfirmationModal);
Vue.component('favorite-creator', FavoriteCreator);
Vue.component('keyboard-shortcuts-modal', KeyboardShortcutsModal);
Vue.component('resource-deleter', ResourceDeleter);
Vue.component('field-action-modal', FieldActionModal);

Vue.component('stack', Stack);
Vue.component('stack-test', StackTest);
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/field-actions/FieldAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default class FieldAction {
return typeof this.#icon === 'function' ? this.#icon(this.#payload) : this.#icon;
}

run() {
async run() {
this.#run(this.#payload);
}
}
160 changes: 160 additions & 0 deletions resources/js/components/field-actions/FieldActionModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<template>

<div>
<confirmation-modal
:title="title"
:danger="dangerous"
:buttonText="buttonText"
:busy="resolving || processing"
@confirm="confirm"
@cancel="cancel"
>
<div class="min-h-20">

<div v-if="confirmationText" v-text="confirmationText" :class="{ 'mb-4': warningText || showDirtyWarning || hasFields }" />

<div v-if="warningText" v-text="warningText" class="text-red-500" :class="{ 'mb-4': showDirtyWarning || hasFields }" />

<div v-if="showDirtyWarning" v-text="dirtyText" class="text-red-500" :class="{ 'mb-4': hasFields }" />

<publish-container
v-if="hasFields && !resolving"
name="confirm-action"
:blueprint="fieldset"
:values="values"
:meta="meta"
:errors="errors"
@updated="values = $event"
>
<publish-fields
slot-scope="{ setFieldValue, setFieldMeta }"
:fields="fieldset.tabs[0].fields"
@updated="setFieldValue"
@meta-updated="setFieldMeta"
/>
</publish-container>

</div>
</confirmation-modal>
</div>

</template>

<script>
export default {
props: {
fields: {
type: Object,
},
title: {
type: String,
},
buttonText: {
type: String,
},
confirmationText: {
type: String,
},
warningText: {
type: String,
},
dirtyText: {
type: String,
},
dirtyWarningText: {
type: String,
},
dangerous: {
type: Boolean,
default: false,
},
bypassesDirtyWarning: {
type: Boolean,
default: false,
},
keepOpen: {
type: Boolean,
default: false,
},
},
data() {
return {
resolving: this.hasFields,
processing: false,
fieldset: [],
values: {},
meta: {},
error: null,
errors: {},
isDirty: false,
}
},
mounted() {
this.initialize()
},
computed: {
hasFields() {
return Object.keys(this.fields).length > 0;
},
showDirtyWarning() {
return this.isDirty && this.dirtyWarningText && ! this.bypassesDirtyWarning;
},
},
methods: {
initialize() {
if (!this.hasFields) {
return;
}
this.resolving = true;
this.$axios.post(cp_url(`field-action-modal/resolve`), {
fields: this.fields,
}).then(response => {
this.fieldset = { tabs: [{ fields: response.data.fieldset }]};
this.values = response.data.values;
this.meta = response.data.meta;
this.resolving = false;
});
},
confirm() {
if (!this.hasFields) {
return;
}
this.processing = true;
this.$axios.post(cp_url('field-action-modal/process'), {
fields: this.fields,
values: this.values,
}).then(response => {
this.processing = this.keepOpen;
this.$emit('confirm', response.data);
}).catch(e => {
this.processing = false;
if (e.response && e.response.status === 422) {
const { message, errors } = e.response.data;
this.error = message;
this.errors = errors;
this.$toast.error(message);
} else if (e.response) {
this.$toast.error(e.response.data.message);
} else {
this.$toast.error(__('Something went wrong'));
}
});
},
cancel() {
this.$emit('cancel')
},
},
}
</script>
25 changes: 25 additions & 0 deletions resources/js/components/field-actions/FieldActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ class FieldActions {
get(name) {
return this.actions[name] || [];
}

modal(props) {
const name = props.fields ? 'field-action-modal' : 'confirmation-modal';
return new Promise((resolve) => {
const component = Statamic.$components.append(name, { props });
component.on('confirm', (data = {}) => {
if (props.keepOpen) {
resolve({
...data,
confirmed: true,
close: () => Statamic.$components.destroy(component.id),
});
} else {
resolve(data);
Statamic.$components.destroy(component.id);
}
});
component.on('cancel', () => {
resolve({
confirmed: false,
});
Statamic.$components.destroy(component.id);
});
});
}
}

export default FieldActions;
6 changes: 6 additions & 0 deletions routes/cp.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
use Statamic\Http\Controllers\CP\CpController;
use Statamic\Http\Controllers\CP\DashboardController;
use Statamic\Http\Controllers\CP\DuplicatesController;
use Statamic\Http\Controllers\CP\FieldActionModalController;
use Statamic\Http\Controllers\CP\Fields\BlueprintController;
use Statamic\Http\Controllers\CP\Fields\FieldsController;
use Statamic\Http\Controllers\CP\Fields\FieldsetController;
Expand Down Expand Up @@ -319,6 +320,11 @@
Route::get('dictionaries/{dictionary}', DictionaryFieldtypeController::class)->name('dictionary-fieldtype');
});

Route::group(['prefix' => 'field-action-modal'], function () {
Route::post('resolve', [FieldActionModalController::class, 'resolve'])->name('resolve');
Route::post('process', [FieldActionModalController::class, 'process'])->name('process');
});

Route::group(['prefix' => 'api', 'as' => 'api.'], function () {
Route::resource('addons', AddonsApiController::class)->only('index');
Route::resource('templates', TemplatesController::class)->only('index');
Expand Down
48 changes: 48 additions & 0 deletions src/Http/Controllers/CP/FieldActionModalController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Statamic\Http\Controllers\CP;

use Illuminate\Http\Request;
use Statamic\Fields\Fields;

class FieldActionModalController extends CpController
{
public function resolve(Request $request)
{
$fields = $this->getFields($request->fields)
->preProcess();

return [
'fieldset' => $fields->toPublishArray(),
'values' => $fields->values(),
'meta' => $fields->meta(),
];
}

public function process(Request $request)
{
$fields = $this->getFields($request->fields)
->addValues($request->values);

$fields->validate();

$raw = $fields->process()->values();

$fields->preProcess();

return [
'values' => $fields->values(),
'meta' => $fields->meta(),
'raw' => $raw,
];
}

protected function getFields($fieldItems)
{
$fields = collect($fieldItems)->map(function ($field, $handle) {
return compact('handle', 'field');
});

return new Fields($fields);
}
}

0 comments on commit 089a76f

Please sign in to comment.